"`):
+ The unknown token. A token that is not in the vocabulary cannot be converted to an ID and is set to be this
+ token instead.
+ bos_token (`str`, *optional*, defaults to `""`):
+ The beginning of sequence token that was used during pretraining. Can be used a sequence classifier token.
+ eos_token (`str`, *optional*, defaults to `""`):
+ The end of sequence token.
+ prefix_token (`str`, *optional*, defaults to `"▁"`):
+ Prefix token used for infilling.
+ middle_token (`str`, *optional*, defaults to `"▁"`):
+ Middle token used for infilling.
+ suffix_token (`str`, *optional*, defaults to `"▁"`):
+ Suffix token used for infilling.
+ eot_token (`str`, *optional*, defaults to `"▁"`):
+ End of text token used for infilling.
+ fill_token (`str`, *optional*, defaults to `""`):
+ The token used to split the input between the prefix and suffix.
+ additional_special_tokens (`list[str]`, *optional*):
+ Additional special tokens used by the tokenizer.
+ add_bos_token (`bool`, *optional*, defaults to `True`):
+ Whether to add a beginning of sequence token at the start of sequences.
+ add_eos_token (`bool`, *optional*, defaults to `False`):
+ Whether to add an end of sequence token at the end of sequences.
+ use_default_system_prompt (`bool`, *optional*, defaults to `False`):
+ Whether or not the default system prompt for Llama should be used.
+ add_prefix_space (`bool`, *optional*):
+ Whether or not to add an initial space to the input. This allows to treat the leading word just as any
+ other word.
+ vocab (`str`, `dict` or `list`, *optional*):
+ Custom vocabulary dictionary. If not provided, vocabulary is loaded from vocab_file.
+ merges (`str` or `list`, *optional*):
+ Custom merges list. If not provided, merges are loaded from merges_file.
+ vocab_file (`str`, *optional*):
+ [SentencePiece](https://github.com/google/sentencepiece) file (generally has a .model extension) that
+ contains the vocabulary necessary to instantiate a tokenizer.
+ """
+
+ vocab_files_names = VOCAB_FILES_NAMES
+ padding_side = "left"
+ model_input_names = ["input_ids", "attention_mask"]
+ model = BPE
+
+ def __init__(
+ self,
+ vocab: str | dict[str, int] | None = None,
+ merges: str | list[str] | None = None,
+ clean_up_tokenization_spaces=False,
+ unk_token="",
+ bos_token="",
+ eos_token="",
+ prefix_token="▁",
+ middle_token="▁",
+ suffix_token="▁",
+ eot_token="▁",
+ fill_token="",
+ additional_special_tokens=None,
+ use_default_system_prompt: bool = False,
+ add_prefix_space: bool | None = True,
+ add_bos_token: bool = True,
+ **kwargs,
+ ):
+ self.add_prefix_space = add_prefix_space if add_prefix_space is not None else True
+ self.use_default_system_prompt = use_default_system_prompt
+ additional_special_tokens = additional_special_tokens or []
+ for token in [prefix_token, middle_token, suffix_token, eot_token, fill_token]:
+ additional_special_tokens += [token] if token is not None else []
+
+ self._vocab = (
+ vocab
+ if vocab is not None
+ else {
+ str(unk_token): 0,
+ str(bos_token): 1,
+ str(eos_token): 2,
+ }
+ )
+
+ self._merges = merges or []
+ self._tokenizer = Tokenizer(
+ BPE(
+ vocab=self._vocab,
+ merges=self._merges,
+ fuse_unk=True,
+ byte_fallback=True,
+ dropout=None,
+ unk_token=str(unk_token),
+ )
+ )
+ prepend_scheme = "first" if self.add_prefix_space else "never"
+ self._tokenizer.pre_tokenizer = pre_tokenizers.Metaspace(
+ replacement="▁", prepend_scheme=prepend_scheme, split=False
+ )
+
+ self._tokenizer.decoder = decoders.Sequence(
+ [decoders.Replace("▁", " "), decoders.ByteFallback(), decoders.Fuse(), decoders.Strip(content=" ", left=1)]
+ )
+
+ super().__init__(
+ clean_up_tokenization_spaces=clean_up_tokenization_spaces,
+ unk_token=unk_token,
+ bos_token=bos_token,
+ eos_token=eos_token,
+ use_default_system_prompt=use_default_system_prompt,
+ add_prefix_space=add_prefix_space,
+ prefix_token=prefix_token,
+ middle_token=middle_token,
+ suffix_token=suffix_token,
+ eot_token=eot_token,
+ fill_token=fill_token,
+ add_bos_token=add_bos_token,
+ additional_special_tokens=additional_special_tokens,
+ **kwargs,
+ )
+ self._prefix_token = prefix_token
+ self._middle_token = middle_token
+ self._suffix_token = suffix_token
+ self._eot_token = eot_token
+ self.fill_token = fill_token
+
+ @property
+ def prefix_token(self):
+ return self._prefix_token
+
+ @property
+ def prefix_id(self):
+ if self._prefix_token is None:
+ return None
+ return self.convert_tokens_to_ids(self.prefix_token)
+
+ @property
+ def middle_token(self):
+ return self._middle_token
+
+ @property
+ def middle_id(self):
+ if self._middle_token is None:
+ return None
+ return self.convert_tokens_to_ids(self.middle_token)
+
+ @property
+ def suffix_token(self):
+ return self._suffix_token
+
+ @property
+ def suffix_id(self):
+ if self._suffix_token is None:
+ return None
+ return self.convert_tokens_to_ids(self.suffix_token)
+
+ @property
+ def eot_id(self):
+ if self._eot_token is None:
+ return None
+ return self.convert_tokens_to_ids(self.eot_token)
+
+ @property
+ def eot_token(self):
+ return self._eot_token
+
+ def set_infilling_processor(self, reset, suffix_first=False, add_special_tokens=True):
+ """
+ Updates the normalizer to make sure the prompt format for `infilling` is respected. The infilling format is the
+ following: if suffix_first
+ " {suf} {pre}"
+ else:
+ " {pre} {suf} "
+
+ If `reset` is set to `True`, the `normalizer` and `post_processor` are reset to their "normal" behaviour, which
+ is to add a prefix space for the normalizer, and add a `bos_token` to the input text for the `post_processor`.
+ """
+ if reset:
+ self._tokenizer.normalizer = normalizers.Sequence(
+ [
+ normalizers.Prepend(prepend="▁"),
+ normalizers.Replace(pattern=" ", content="▁"),
+ ]
+ )
+ self.update_post_processor()
+ return
+
+ self._tokenizer.normalizer = normalizers.Replace(pattern=" ", content="▁")
+ pair = [self.bos_token] if self.add_bos_token and add_special_tokens else []
+ special_tokens = [(self.bos_token, self.bos_token_id)] if self.add_bos_token and add_special_tokens else []
+ if suffix_first:
+ # format as " {suf} {pre}"
+ pair += [self.prefix_token, self.suffix_token, "$B", self.middle_token, "$A"]
+ special_tokens += [
+ (self.prefix_token, self.prefix_id),
+ (self.suffix_token, self.suffix_id),
+ (self.middle_token, self.middle_id),
+ ]
+ else:
+ # format as " {pre} {suf} "
+ pair += [self.prefix_token, "$A", self.suffix_token, "$B", self.middle_token]
+ special_tokens += [
+ (self.prefix_token, self.prefix_id),
+ (self.suffix_token, self.suffix_id),
+ (self.middle_token, self.middle_id),
+ ]
+
+ if self.add_eos_token and add_special_tokens:
+ pair += [self.eos_token]
+ special_tokens += [(self.eos_token, self.eos_token_id)]
+ self._tokenizer.post_processor = processors.TemplateProcessing(
+ single="$A", pair=pair, special_tokens=special_tokens
+ )
+
+ def tokenize(self, text, suffix=None, suffix_first=False, **kwargs):
+ # Handle fill_token splitting
+ if self.fill_token is not None and self.fill_token in text and suffix is None:
+ text, suffix = text.split(self.fill_token)
+
+ # If no suffix, use standard tokenization
+ if suffix is None or len(suffix) < 1:
+ return super().tokenize(text, **kwargs)
+
+ # Check that infilling tokens are available
+ if None in (self.prefix_id, self.middle_id, self.suffix_id):
+ raise ValueError(
+ "The input either includes a `prefix` and a `suffix` used for the infilling task,"
+ f" or can be split on the {self.fill_token} token, creating a suffix and prefix,"
+ " but the model does not support `infilling`."
+ )
+
+ # Temporarily set infilling processor
+ self.set_infilling_processor(False, suffix_first=suffix_first, add_special_tokens=False)
+
+ # Remove text_pair and pair from kwargs if present to avoid conflict
+ kwargs.pop("text_pair", None)
+ kwargs.pop("pair", None)
+
+ # Tokenize with infilling format
+ # The processor will handle the special token arrangement
+ # Use pair=suffix (not text_pair) since base class tokenize expects 'pair' parameter
+ result = super().tokenize(" " + text, pair=suffix, **kwargs)
+
+ # Reset processor
+ self.set_infilling_processor(True)
+
+ return result
+
+ def _encode_plus(self, text, text_pair=None, suffix=None, suffix_first=False, add_special_tokens=True, **kwargs):
+ is_infilling = False
+
+ if suffix is not None:
+ text_pair = suffix
+ is_infilling = True
+ elif "suffix" in kwargs:
+ text_pair = kwargs.pop("suffix")
+ is_infilling = True
+
+ if isinstance(text, str) and self.fill_token is not None and self.fill_token in text and text_pair is None:
+ text, text_pair = text.split(self.fill_token)
+ is_infilling = True
+
+ if not is_infilling:
+ return super()._encode_plus(text, text_pair=text_pair, add_special_tokens=add_special_tokens, **kwargs)
+
+ if (
+ text_pair is None
+ or (isinstance(text_pair, str) and len(text_pair) < 1)
+ or (isinstance(text_pair, list) and len(text_pair) == 0)
+ ):
+ return super()._encode_plus(text, text_pair=text_pair, add_special_tokens=add_special_tokens, **kwargs)
+
+ if None in (self.prefix_id, self.middle_id, self.suffix_id):
+ raise ValueError(
+ "The input includes a `prefix` and a `suffix` used for the infilling task,"
+ " the `prefix_id, middle_id, suffix_id` must all be initialized. Current"
+ f" values : {self.prefix_id, self.middle_id, self.suffix_id}"
+ )
+
+ self.set_infilling_processor(False, suffix_first=suffix_first, add_special_tokens=add_special_tokens)
+ kwargs.pop("text_pair", None)
+
+ if isinstance(text, str):
+ text = " " + text
+ elif isinstance(text, list):
+ text = [" " + t if isinstance(t, str) else t for t in text]
+
+ result = super()._encode_plus(text, text_pair=text_pair, add_special_tokens=True, **kwargs)
+ self.set_infilling_processor(True)
+ return result
+
+
+__all__ = ["CodeLlamaTokenizer", "CodeLlamaTokenizerFast"]
+
+# Backward alias
+CodeLlamaTokenizerFast = CodeLlamaTokenizer
diff --git a/third_party/transformers/src/transformers/models/cohere/__init__.py b/third_party/transformers/src/transformers/models/cohere/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..98c73f4cd22dde96014bb3fb8139f242d413e802
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/cohere/__init__.py
@@ -0,0 +1,28 @@
+# Copyright 2024 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_cohere import *
+ from .modeling_cohere import *
+ from .tokenization_cohere import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/cohere/configuration_cohere.py b/third_party/transformers/src/transformers/models/cohere/configuration_cohere.py
new file mode 100644
index 0000000000000000000000000000000000000000..d52a3e00842761c8a7c12fdfcd20ee89d7c8d422
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/cohere/configuration_cohere.py
@@ -0,0 +1,94 @@
+# Copyright 2024 Cohere team. All rights reserved.
+#
+# This code is based on EleutherAI's GPT-NeoX library and the GPT-NeoX
+# and OPT implementations in this library. It has been modified from its
+# original forms to accommodate minor architectural differences compared
+# to GPT-NeoX and OPT used by the Meta AI team that trained the model.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Cohere model configuration"""
+
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...modeling_rope_utils import RopeParameters
+from ...utils import auto_docstring
+
+
+@auto_docstring(checkpoint="CohereForAI/c4ai-command-r-v01")
+@strict
+class CohereConfig(PreTrainedConfig):
+ r"""
+ logit_scale (`float`, *optional*, defaults to 0.0625):
+ The scaling factor for the output logits.
+
+ ```python
+ >>> from transformers import CohereModel, CohereConfig
+
+ >>> # Initializing a Cohere model configuration
+ >>> configuration = CohereConfig()
+
+ >>> # Initializing a model from the Cohere configuration
+ >>> model = CohereModel(configuration) # doctest: +SKIP
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config # doctest: +SKIP
+ ```
+ """
+
+ model_type = "cohere"
+ keys_to_ignore_at_inference = ["past_key_values"]
+ default_theta = 500000.0
+ base_model_tp_plan = {
+ "layers.*.self_attn.q_proj": "colwise",
+ "layers.*.self_attn.k_proj": "colwise",
+ "layers.*.self_attn.v_proj": "colwise",
+ "layers.*.self_attn.o_proj": "rowwise",
+ "layers.*.mlp.gate_proj": "colwise",
+ "layers.*.mlp.up_proj": "colwise",
+ "layers.*.mlp.down_proj": "rowwise",
+ }
+ base_model_pp_plan = {
+ "embed_tokens": (["input_ids"], ["inputs_embeds"]),
+ "layers": (["hidden_states", "attention_mask"], ["hidden_states"]),
+ "norm": (["hidden_states"], ["hidden_states"]),
+ }
+ vocab_size: int = 256000
+ hidden_size: int = 8192
+ intermediate_size: int = 22528
+ logit_scale: float | None = 0.0625
+ num_hidden_layers: int = 40
+ num_attention_heads: int = 64
+ num_key_value_heads: int | None = None
+ hidden_act: str = "silu"
+ max_position_embeddings: int = 8192
+ initializer_range: float = 0.02
+ layer_norm_eps: float | None = 1e-5
+ use_cache: bool = True
+ pad_token_id: int | None = 0
+ bos_token_id: int | None = 5
+ eos_token_id: int | list[int] | None = 255001
+ tie_word_embeddings: bool = True
+ rope_parameters: RopeParameters | dict | None = None
+ attention_bias: bool = False
+ attention_dropout: float | int | None = 0.0
+ use_qk_norm: bool | None = False
+
+ def __post_init__(self, **kwargs):
+ if self.num_key_value_heads is None:
+ self.num_key_value_heads = self.num_attention_heads
+
+ super().__post_init__(**kwargs)
+
+
+__all__ = ["CohereConfig"]
diff --git a/third_party/transformers/src/transformers/models/cohere/modeling_cohere.py b/third_party/transformers/src/transformers/models/cohere/modeling_cohere.py
new file mode 100644
index 0000000000000000000000000000000000000000..b8bf50af9bf4da1ad78a44934463658d9e430110
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/cohere/modeling_cohere.py
@@ -0,0 +1,530 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/cohere/modular_cohere.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_cohere.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2024 Cohere team. All rights reserved.
+#
+# This code is based on EleutherAI's GPT-NeoX library and the GPT-NeoX
+# and OPT implementations in this library. It has been modified from its
+# original forms to accommodate minor architectural differences compared
+# to GPT-NeoX and OPT used by the Meta AI team that trained the model.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This file is based on the LLama model definition file in transformers
+
+
+from collections.abc import Callable
+from typing import Optional
+
+import torch
+from torch import nn
+
+from ...activations import ACT2FN
+from ...cache_utils import Cache, DynamicCache
+from ...generation import GenerationMixin
+from ...integrations import use_kernelized_func
+from ...masking_utils import create_causal_mask
+from ...modeling_flash_attention_utils import FlashAttentionKwargs
+from ...modeling_layers import GradientCheckpointingLayer
+from ...modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast
+from ...modeling_rope_utils import ROPE_INIT_FUNCTIONS, dynamic_rope_update
+from ...modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
+from ...processing_utils import Unpack
+from ...utils import TransformersKwargs, auto_docstring, can_return_tuple
+from ...utils.generic import maybe_autocast, merge_with_config_defaults
+from ...utils.output_capturing import capture_outputs
+from .configuration_cohere import CohereConfig
+
+
+class CohereLayerNorm(nn.Module):
+ def __init__(self, hidden_size=None, eps=1e-5, bias=False):
+ """The hidden size can be a tuple or an int. The tuple is used for QKNorm to normalize across head_dim"""
+ super().__init__()
+ self.weight = nn.Parameter(torch.ones(hidden_size))
+ self.variance_epsilon = eps
+
+ def forward(self, hidden_states):
+ input_dtype = hidden_states.dtype
+ hidden_states = hidden_states.to(torch.float32)
+ mean = hidden_states.mean(-1, keepdim=True)
+ variance = (hidden_states - mean).pow(2).mean(-1, keepdim=True)
+ hidden_states = (hidden_states - mean) * torch.rsqrt(variance + self.variance_epsilon)
+ hidden_states = self.weight.to(torch.float32) * hidden_states
+ return hidden_states.to(input_dtype)
+
+
+class CohereRotaryEmbedding(nn.Module):
+ inv_freq: torch.Tensor # fix linting for `register_buffer`
+
+ def __init__(self, config: CohereConfig, device=None):
+ super().__init__()
+ self.max_seq_len_cached = config.max_position_embeddings
+ self.original_max_seq_len = config.max_position_embeddings
+
+ self.config = config
+
+ self.rope_type = self.config.rope_parameters["rope_type"]
+ rope_init_fn: Callable = self.compute_default_rope_parameters
+ if self.rope_type != "default":
+ rope_init_fn = ROPE_INIT_FUNCTIONS[self.rope_type]
+ inv_freq, self.attention_scaling = rope_init_fn(self.config, device)
+
+ self.register_buffer("inv_freq", inv_freq, persistent=False)
+ self.register_buffer("original_inv_freq", inv_freq.clone(), persistent=False)
+
+ @staticmethod
+ def compute_default_rope_parameters(
+ config: CohereConfig | None = None,
+ device: Optional["torch.device"] = None,
+ seq_len: int | None = None,
+ ) -> tuple["torch.Tensor", float]:
+ """
+ Computes the inverse frequencies according to the original RoPE implementation
+ Args:
+ config ([`~transformers.PreTrainedConfig`]):
+ The model configuration.
+ device (`torch.device`):
+ The device to use for initialization of the inverse frequencies.
+ seq_len (`int`, *optional*):
+ The current sequence length. Unused for this type of RoPE.
+ Returns:
+ Tuple of (`torch.Tensor`, `float`), containing the inverse frequencies for the RoPE embeddings and the
+ post-processing scaling factor applied to the computed cos/sin (unused in this type of RoPE).
+ """
+ base = config.rope_parameters["rope_theta"]
+ dim = getattr(config, "head_dim", None) or config.hidden_size // config.num_attention_heads
+
+ attention_factor = 1.0 # Unused in this type of RoPE
+
+ # Compute the inverse frequencies
+ inv_freq = 1.0 / (
+ base ** (torch.arange(0, dim, 2, dtype=torch.int64).to(device=device, dtype=torch.float) / dim)
+ )
+ return inv_freq, attention_factor
+
+ @torch.no_grad()
+ @dynamic_rope_update # power user: used with advanced RoPE types (e.g. dynamic rope)
+ def forward(self, x, position_ids):
+ inv_freq_expanded = self.inv_freq[None, :, None].float().expand(position_ids.shape[0], -1, 1)
+ position_ids_expanded = position_ids[:, None, :].float()
+
+ device_type = x.device.type if isinstance(x.device.type, str) and x.device.type != "mps" else "cpu"
+ with maybe_autocast(device_type=device_type, enabled=False): # Force float32
+ freqs = (inv_freq_expanded.float() @ position_ids_expanded.float()).transpose(1, 2)
+ emb = torch.repeat_interleave(freqs, 2, dim=-1) # diff from Llama: we interleave() instead of cat()
+ cos = emb.cos() * self.attention_scaling
+ sin = emb.sin() * self.attention_scaling
+
+ return cos.to(dtype=x.dtype), sin.to(dtype=x.dtype)
+
+
+class CohereMLP(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+ self.config = config
+ self.hidden_size = config.hidden_size
+ self.intermediate_size = config.intermediate_size
+ self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
+ self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
+ self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False)
+ self.act_fn = ACT2FN[config.hidden_act]
+
+ def forward(self, x):
+ down_proj = self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
+ return down_proj
+
+
+def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
+ """
+ This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
+ num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
+ """
+ batch, num_key_value_heads, slen, head_dim = hidden_states.shape
+ if n_rep == 1:
+ return hidden_states
+ hidden_states = hidden_states[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim)
+ return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
+
+
+def eager_attention_forward(
+ module: nn.Module,
+ query: torch.Tensor,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ attention_mask: torch.Tensor | None,
+ scaling: float,
+ dropout: float = 0.0,
+ **kwargs: Unpack[TransformersKwargs],
+):
+ key_states = repeat_kv(key, module.num_key_value_groups)
+ value_states = repeat_kv(value, module.num_key_value_groups)
+
+ attn_weights = torch.matmul(query, key_states.transpose(2, 3)) * scaling
+ if attention_mask is not None:
+ attn_weights = attn_weights + attention_mask
+
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query.dtype)
+ attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
+ attn_output = torch.matmul(attn_weights, value_states)
+ attn_output = attn_output.transpose(1, 2).contiguous()
+
+ return attn_output, attn_weights
+
+
+def rotate_half(x):
+ # Split and rotate. Note that this function is different from e.g. Llama.
+ x1 = x[..., ::2]
+ x2 = x[..., 1::2]
+ rot_x = torch.stack([-x2, x1], dim=-1).flatten(-2)
+ return rot_x
+
+
+def apply_rotary_pos_emb(q, k, cos, sin, unsqueeze_dim=1):
+ """Applies Rotary Position Embedding to the query and key tensors.
+
+ Args:
+ q (`torch.Tensor`): The query tensor.
+ k (`torch.Tensor`): The key tensor.
+ cos (`torch.Tensor`): The cosine part of the rotary embedding.
+ sin (`torch.Tensor`): The sine part of the rotary embedding.
+ unsqueeze_dim (`int`, *optional*, defaults to 1):
+ The 'unsqueeze_dim' argument specifies the dimension along which to unsqueeze cos[position_ids] and
+ sin[position_ids] so that they can be properly broadcasted to the dimensions of q and k. For example, note
+ that cos[position_ids] and sin[position_ids] have the shape [batch_size, seq_len, head_dim]. Then, if q and
+ k have the shape [batch_size, heads, seq_len, head_dim], then setting unsqueeze_dim=1 makes
+ cos[position_ids] and sin[position_ids] broadcastable to the shapes of q and k. Similarly, if q and k have
+ the shape [batch_size, seq_len, heads, head_dim], then set unsqueeze_dim=2.
+ Returns:
+ `tuple(torch.Tensor)` comprising of the query and key tensors rotated using the Rotary Position Embedding.
+ """
+ dtype = q.dtype
+ q = q.float()
+ k = k.float()
+ cos = cos.unsqueeze(unsqueeze_dim)
+ sin = sin.unsqueeze(unsqueeze_dim)
+ q_embed = (q * cos) + (rotate_half(q) * sin)
+ k_embed = (k * cos) + (rotate_half(k) * sin)
+ return q_embed.to(dtype=dtype), k_embed.to(dtype=dtype)
+
+
+@use_kernelized_func(apply_rotary_pos_emb)
+class CohereAttention(nn.Module):
+ """Multi-headed attention from 'Attention Is All You Need' paper"""
+
+ def __init__(self, config: CohereConfig, layer_idx: int | None = None):
+ super().__init__()
+ self.config = config
+ self.layer_idx = layer_idx
+ self.head_dim = getattr(config, "head_dim", config.hidden_size // config.num_attention_heads)
+ self.num_key_value_groups = config.num_attention_heads // config.num_key_value_heads
+ self.scaling = self.head_dim**-0.5
+ self.attention_dropout = config.attention_dropout
+ self.is_causal = True
+
+ self.q_proj = nn.Linear(
+ config.hidden_size, config.num_attention_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.k_proj = nn.Linear(
+ config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.v_proj = nn.Linear(
+ config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.o_proj = nn.Linear(
+ config.num_attention_heads * self.head_dim, config.hidden_size, bias=config.attention_bias
+ )
+ self.use_qk_norm = config.use_qk_norm
+ if self.use_qk_norm:
+ # When sharding the model using Tensor Parallelism, need to be careful to use n_local_heads
+ self.q_norm = CohereLayerNorm(
+ hidden_size=(config.num_attention_heads, self.head_dim), eps=config.layer_norm_eps
+ )
+ self.k_norm = CohereLayerNorm(
+ hidden_size=(config.num_key_value_heads, self.head_dim), eps=config.layer_norm_eps
+ )
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor],
+ attention_mask: torch.Tensor | None,
+ past_key_values: Cache | None = None,
+ **kwargs: Unpack[FlashAttentionKwargs],
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
+ input_shape = hidden_states.shape[:-1]
+ hidden_shape = (*input_shape, -1, self.head_dim)
+
+ query_states = self.q_proj(hidden_states).view(hidden_shape)
+ key_states = self.k_proj(hidden_states).view(hidden_shape)
+ value_states = self.v_proj(hidden_states).view(hidden_shape)
+
+ if self.use_qk_norm: # main diff from Llama
+ query_states = self.q_norm(query_states)
+ key_states = self.k_norm(key_states)
+
+ query_states = query_states.transpose(1, 2)
+ key_states = key_states.transpose(1, 2)
+ value_states = value_states.transpose(1, 2)
+
+ cos, sin = position_embeddings
+ query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin)
+
+ if past_key_values is not None:
+ key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx)
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ query_states,
+ key_states,
+ value_states,
+ attention_mask,
+ dropout=0.0 if not self.training else self.attention_dropout,
+ scaling=self.scaling,
+ **kwargs,
+ )
+
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
+ attn_output = self.o_proj(attn_output)
+ return attn_output, attn_weights
+
+
+class CohereDecoderLayer(GradientCheckpointingLayer):
+ def __init__(self, config: CohereConfig, layer_idx: int):
+ super().__init__()
+ self.hidden_size = config.hidden_size
+ self.self_attn = CohereAttention(config=config, layer_idx=layer_idx)
+ self.mlp = CohereMLP(config)
+ self.input_layernorm = CohereLayerNorm(hidden_size=(config.hidden_size), eps=config.layer_norm_eps)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ use_cache: bool | None = False,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
+ **kwargs: Unpack[FlashAttentionKwargs],
+ ) -> tuple[torch.FloatTensor, tuple[torch.FloatTensor, torch.FloatTensor] | None]:
+ """
+ Args:
+ hidden_states (`torch.FloatTensor`): input to the layer of shape `(batch, seq_len, embed_dim)`
+ attention_mask (`torch.FloatTensor`, *optional*):
+ attention mask of size `(batch_size, sequence_length)` if flash attention is used or `(batch_size, 1,
+ query_sequence_length, key_sequence_length)` if default attention is used.
+ past_key_values (`Cache`, *optional*): cached past key and value projection states
+ output_attentions (`bool`, *optional*):
+ Whether or not to return the attentions tensors of all attention layers. See `attentions` under
+ returned tensors for more detail.
+ use_cache (`bool`, *optional*):
+ If set to `True`, `past_key_values` key value states are returned and can be used to speed up decoding
+ (see `past_key_values`).
+ position_embeddings (`tuple[torch.FloatTensor, torch.FloatTensor]`, *optional*):
+ Tuple containing the cosine and sine positional embeddings of shape `(batch_size, seq_len, head_dim)`,
+ with `head_dim` being the embedding dimension of each attention head.
+ """
+ residual = hidden_states
+ hidden_states = self.input_layernorm(hidden_states)
+
+ hidden_states_attention, _ = self.self_attn(
+ hidden_states=hidden_states,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ position_embeddings=position_embeddings,
+ **kwargs,
+ )
+
+ hidden_states_mlp = self.mlp(hidden_states)
+ hidden_states = residual + hidden_states_attention + hidden_states_mlp
+ return hidden_states
+
+
+@auto_docstring
+class CoherePreTrainedModel(PreTrainedModel):
+ config: CohereConfig
+ base_model_prefix = "model"
+ supports_gradient_checkpointing = True
+ _no_split_modules = ["CohereDecoderLayer"]
+ _skip_keys_device_placement = ["past_key_values"]
+ _supports_flash_attn = True
+ _supports_sdpa = True
+ _supports_flex_attn = True
+
+ _can_compile_fullgraph = True
+ _supports_attention_backend = True
+ _can_record_outputs = {
+ "hidden_states": CohereDecoderLayer,
+ "attentions": CohereAttention,
+ }
+
+
+@auto_docstring
+class CohereModel(CoherePreTrainedModel):
+ def __init__(self, config: CohereConfig):
+ super().__init__(config)
+ self.padding_idx = config.pad_token_id
+ self.vocab_size = config.vocab_size
+
+ self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)
+ self.layers = nn.ModuleList(
+ [CohereDecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
+ )
+ self.norm = CohereLayerNorm(hidden_size=(config.hidden_size), eps=config.layer_norm_eps)
+ self.rotary_emb = CohereRotaryEmbedding(config=config)
+ self.gradient_checkpointing = False
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @merge_with_config_defaults
+ @capture_outputs
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ use_cache: bool | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> BaseModelOutputWithPast:
+ 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 None:
+ inputs_embeds: torch.Tensor = self.embed_tokens(input_ids)
+
+ if use_cache and past_key_values is None:
+ past_key_values = DynamicCache(config=self.config)
+
+ if position_ids is None:
+ past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
+ position_ids = torch.arange(inputs_embeds.shape[1], device=inputs_embeds.device) + past_seen_tokens
+ position_ids = position_ids.unsqueeze(0)
+
+ causal_mask = create_causal_mask(
+ config=self.config,
+ inputs_embeds=inputs_embeds,
+ attention_mask=attention_mask,
+ past_key_values=past_key_values,
+ position_ids=position_ids,
+ )
+
+ hidden_states = inputs_embeds
+ position_embeddings = self.rotary_emb(hidden_states, position_ids=position_ids)
+
+ for decoder_layer in self.layers[: self.config.num_hidden_layers]:
+ hidden_states = decoder_layer(
+ hidden_states,
+ attention_mask=causal_mask,
+ position_embeddings=position_embeddings,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ hidden_states = self.norm(hidden_states)
+ return BaseModelOutputWithPast(
+ last_hidden_state=hidden_states,
+ past_key_values=past_key_values,
+ )
+
+
+@auto_docstring
+class CohereForCausalLM(CoherePreTrainedModel, GenerationMixin):
+ _tied_weights_keys = {"lm_head.weight": "model.embed_tokens.weight"}
+ _tp_plan = {"lm_head": "colwise_gather_output"}
+ _pp_plan = {"lm_head": (["hidden_states"], ["logits"])}
+
+ def __init__(self, config):
+ super().__init__(config)
+ self.model = CohereModel(config)
+ self.vocab_size = config.vocab_size
+ self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
+ self.logit_scale = config.logit_scale
+ self.tie_word_embeddings = config.tie_word_embeddings
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> CausalLMOutputWithPast:
+ r"""
+ Example:
+
+ ```python
+ >> from transformers import AutoTokenizer, CohereForCausalLM
+
+ >> model = CohereForCausalLM.from_pretrained("CohereForAI/c4ai-command-r-v01")
+ >> tokenizer = AutoTokenizer.from_pretrained("CohereForAI/c4ai-command-r-v01")
+
+ >> prompt = "Hey, are you conscious? Can you talk to me?"
+ >> inputs = tokenizer(prompt, return_tensors="pt")
+
+ >> # Generate
+ >> generate_ids = model.generate(inputs.input_ids, max_length=30)
+ >> tokenizer.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]
+ "Hey, are you conscious? Can you talk to me?\nI'm not conscious, but I can talk to you."
+ ```"""
+ outputs: BaseModelOutputWithPast = self.model(
+ input_ids=input_ids,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ hidden_states = outputs.last_hidden_state
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
+ logits = logits * self.logit_scale # main diff from Llama
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(logits=logits, labels=labels, vocab_size=self.config.vocab_size, **kwargs)
+
+ return CausalLMOutputWithPast(
+ loss=loss,
+ logits=logits,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ )
+
+
+__all__ = ["CohereForCausalLM", "CohereModel", "CoherePreTrainedModel"]
diff --git a/third_party/transformers/src/transformers/models/cohere/modular_cohere.py b/third_party/transformers/src/transformers/models/cohere/modular_cohere.py
new file mode 100644
index 0000000000000000000000000000000000000000..b7abbf1fc77e1ee283bf8ec527e7fa94ef184d29
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/cohere/modular_cohere.py
@@ -0,0 +1,326 @@
+# Copyright 2024 Cohere team. All rights reserved.
+#
+# This code is based on EleutherAI's GPT-NeoX library and the GPT-NeoX
+# and OPT implementations in this library. It has been modified from its
+# original forms to accommodate minor architectural differences compared
+# to GPT-NeoX and OPT used by the Meta AI team that trained the model.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This file is based on the LLama model definition file in transformers
+
+"""PyTorch Cohere model."""
+
+from collections.abc import Callable
+
+import torch
+from torch import nn
+
+from ...cache_utils import Cache
+from ...modeling_flash_attention_utils import FlashAttentionKwargs
+from ...modeling_layers import GradientCheckpointingLayer
+from ...modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast
+from ...modeling_rope_utils import dynamic_rope_update
+from ...modeling_utils import ALL_ATTENTION_FUNCTIONS
+from ...processing_utils import Unpack
+from ...utils import TransformersKwargs, auto_docstring, can_return_tuple, logging
+from ...utils.generic import maybe_autocast
+from ..llama.modeling_llama import (
+ LlamaAttention,
+ LlamaForCausalLM,
+ LlamaMLP,
+ LlamaModel,
+ LlamaRotaryEmbedding,
+ eager_attention_forward,
+)
+from .configuration_cohere import CohereConfig
+
+
+logger = logging.get_logger(__name__)
+
+
+class CohereLayerNorm(nn.Module):
+ def __init__(self, hidden_size=None, eps=1e-5, bias=False):
+ """The hidden size can be a tuple or an int. The tuple is used for QKNorm to normalize across head_dim"""
+ super().__init__()
+ self.weight = nn.Parameter(torch.ones(hidden_size))
+ self.variance_epsilon = eps
+
+ def forward(self, hidden_states):
+ input_dtype = hidden_states.dtype
+ hidden_states = hidden_states.to(torch.float32)
+ mean = hidden_states.mean(-1, keepdim=True)
+ variance = (hidden_states - mean).pow(2).mean(-1, keepdim=True)
+ hidden_states = (hidden_states - mean) * torch.rsqrt(variance + self.variance_epsilon)
+ hidden_states = self.weight.to(torch.float32) * hidden_states
+ return hidden_states.to(input_dtype)
+
+
+class CohereRotaryEmbedding(LlamaRotaryEmbedding):
+ @torch.no_grad()
+ @dynamic_rope_update # power user: used with advanced RoPE types (e.g. dynamic rope)
+ def forward(self, x, position_ids):
+ inv_freq_expanded = self.inv_freq[None, :, None].float().expand(position_ids.shape[0], -1, 1)
+ position_ids_expanded = position_ids[:, None, :].float()
+
+ device_type = x.device.type if isinstance(x.device.type, str) and x.device.type != "mps" else "cpu"
+ with maybe_autocast(device_type=device_type, enabled=False): # Force float32
+ freqs = (inv_freq_expanded.float() @ position_ids_expanded.float()).transpose(1, 2)
+ emb = torch.repeat_interleave(freqs, 2, dim=-1) # diff from Llama: we interleave() instead of cat()
+ cos = emb.cos() * self.attention_scaling
+ sin = emb.sin() * self.attention_scaling
+
+ return cos.to(dtype=x.dtype), sin.to(dtype=x.dtype)
+
+
+def rotate_half(x):
+ # Split and rotate. Note that this function is different from e.g. Llama.
+ x1 = x[..., ::2]
+ x2 = x[..., 1::2]
+ rot_x = torch.stack([-x2, x1], dim=-1).flatten(-2)
+ return rot_x
+
+
+def apply_rotary_pos_emb(q, k, cos, sin, unsqueeze_dim=1):
+ """Applies Rotary Position Embedding to the query and key tensors.
+
+ Args:
+ q (`torch.Tensor`): The query tensor.
+ k (`torch.Tensor`): The key tensor.
+ cos (`torch.Tensor`): The cosine part of the rotary embedding.
+ sin (`torch.Tensor`): The sine part of the rotary embedding.
+ unsqueeze_dim (`int`, *optional*, defaults to 1):
+ The 'unsqueeze_dim' argument specifies the dimension along which to unsqueeze cos[position_ids] and
+ sin[position_ids] so that they can be properly broadcasted to the dimensions of q and k. For example, note
+ that cos[position_ids] and sin[position_ids] have the shape [batch_size, seq_len, head_dim]. Then, if q and
+ k have the shape [batch_size, heads, seq_len, head_dim], then setting unsqueeze_dim=1 makes
+ cos[position_ids] and sin[position_ids] broadcastable to the shapes of q and k. Similarly, if q and k have
+ the shape [batch_size, seq_len, heads, head_dim], then set unsqueeze_dim=2.
+ Returns:
+ `tuple(torch.Tensor)` comprising of the query and key tensors rotated using the Rotary Position Embedding.
+ """
+ dtype = q.dtype
+ q = q.float()
+ k = k.float()
+ cos = cos.unsqueeze(unsqueeze_dim)
+ sin = sin.unsqueeze(unsqueeze_dim)
+ q_embed = (q * cos) + (rotate_half(q) * sin)
+ k_embed = (k * cos) + (rotate_half(k) * sin)
+ return q_embed.to(dtype=dtype), k_embed.to(dtype=dtype)
+
+
+class CohereMLP(LlamaMLP):
+ def __init__(self, config):
+ super().__init__(config)
+ self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
+ self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
+ self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False)
+
+
+class CohereAttention(LlamaAttention):
+ """Multi-headed attention from 'Attention Is All You Need' paper"""
+
+ def __init__(self, config: CohereConfig, layer_idx: int | None = None):
+ super().__init__(config, layer_idx)
+ self.use_qk_norm = config.use_qk_norm
+ if self.use_qk_norm:
+ # When sharding the model using Tensor Parallelism, need to be careful to use n_local_heads
+ self.q_norm = CohereLayerNorm(
+ hidden_size=(config.num_attention_heads, self.head_dim), eps=config.layer_norm_eps
+ )
+ self.k_norm = CohereLayerNorm(
+ hidden_size=(config.num_key_value_heads, self.head_dim), eps=config.layer_norm_eps
+ )
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor],
+ attention_mask: torch.Tensor | None,
+ past_key_values: Cache | None = None,
+ **kwargs: Unpack[FlashAttentionKwargs],
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
+ input_shape = hidden_states.shape[:-1]
+ hidden_shape = (*input_shape, -1, self.head_dim)
+
+ query_states = self.q_proj(hidden_states).view(hidden_shape)
+ key_states = self.k_proj(hidden_states).view(hidden_shape)
+ value_states = self.v_proj(hidden_states).view(hidden_shape)
+
+ if self.use_qk_norm: # main diff from Llama
+ query_states = self.q_norm(query_states)
+ key_states = self.k_norm(key_states)
+
+ query_states = query_states.transpose(1, 2)
+ key_states = key_states.transpose(1, 2)
+ value_states = value_states.transpose(1, 2)
+
+ cos, sin = position_embeddings
+ query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin)
+
+ if past_key_values is not None:
+ key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx)
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ query_states,
+ key_states,
+ value_states,
+ attention_mask,
+ dropout=0.0 if not self.training else self.attention_dropout,
+ scaling=self.scaling,
+ **kwargs,
+ )
+
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
+ attn_output = self.o_proj(attn_output)
+ return attn_output, attn_weights
+
+
+class CohereDecoderLayer(GradientCheckpointingLayer):
+ def __init__(self, config: CohereConfig, layer_idx: int):
+ super().__init__()
+ self.hidden_size = config.hidden_size
+ self.self_attn = CohereAttention(config=config, layer_idx=layer_idx)
+ self.mlp = CohereMLP(config)
+ self.input_layernorm = CohereLayerNorm(hidden_size=(config.hidden_size), eps=config.layer_norm_eps)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ use_cache: bool | None = False,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
+ **kwargs: Unpack[FlashAttentionKwargs],
+ ) -> tuple[torch.FloatTensor, tuple[torch.FloatTensor, torch.FloatTensor] | None]:
+ """
+ Args:
+ hidden_states (`torch.FloatTensor`): input to the layer of shape `(batch, seq_len, embed_dim)`
+ attention_mask (`torch.FloatTensor`, *optional*):
+ attention mask of size `(batch_size, sequence_length)` if flash attention is used or `(batch_size, 1,
+ query_sequence_length, key_sequence_length)` if default attention is used.
+ past_key_values (`Cache`, *optional*): cached past key and value projection states
+ output_attentions (`bool`, *optional*):
+ Whether or not to return the attentions tensors of all attention layers. See `attentions` under
+ returned tensors for more detail.
+ use_cache (`bool`, *optional*):
+ If set to `True`, `past_key_values` key value states are returned and can be used to speed up decoding
+ (see `past_key_values`).
+ position_embeddings (`tuple[torch.FloatTensor, torch.FloatTensor]`, *optional*):
+ Tuple containing the cosine and sine positional embeddings of shape `(batch_size, seq_len, head_dim)`,
+ with `head_dim` being the embedding dimension of each attention head.
+ """
+ residual = hidden_states
+ hidden_states = self.input_layernorm(hidden_states)
+
+ hidden_states_attention, _ = self.self_attn(
+ hidden_states=hidden_states,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ position_embeddings=position_embeddings,
+ **kwargs,
+ )
+
+ hidden_states_mlp = self.mlp(hidden_states)
+ hidden_states = residual + hidden_states_attention + hidden_states_mlp
+ return hidden_states
+
+
+class CohereModel(LlamaModel):
+ def __init__(self, config: CohereConfig):
+ super().__init__(config)
+ self.layers = nn.ModuleList(
+ [CohereDecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
+ )
+ self.norm = CohereLayerNorm(hidden_size=(config.hidden_size), eps=config.layer_norm_eps)
+
+
+class CohereForCausalLM(LlamaForCausalLM):
+ def __init__(self, config):
+ super().__init__(config)
+ self.model = CohereModel(config)
+ self.logit_scale = config.logit_scale
+ self.tie_word_embeddings = config.tie_word_embeddings
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> CausalLMOutputWithPast:
+ r"""
+ Example:
+
+ ```python
+ >> from transformers import AutoTokenizer, CohereForCausalLM
+
+ >> model = CohereForCausalLM.from_pretrained("CohereForAI/c4ai-command-r-v01")
+ >> tokenizer = AutoTokenizer.from_pretrained("CohereForAI/c4ai-command-r-v01")
+
+ >> prompt = "Hey, are you conscious? Can you talk to me?"
+ >> inputs = tokenizer(prompt, return_tensors="pt")
+
+ >> # Generate
+ >> generate_ids = model.generate(inputs.input_ids, max_length=30)
+ >> tokenizer.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]
+ "Hey, are you conscious? Can you talk to me?\nI'm not conscious, but I can talk to you."
+ ```"""
+ outputs: BaseModelOutputWithPast = self.model(
+ input_ids=input_ids,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ hidden_states = outputs.last_hidden_state
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
+ logits = logits * self.logit_scale # main diff from Llama
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(logits=logits, labels=labels, vocab_size=self.config.vocab_size, **kwargs)
+
+ return CausalLMOutputWithPast(
+ loss=loss,
+ logits=logits,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ )
+
+
+__all__ = [
+ "CohereForCausalLM",
+ "CohereModel",
+ "CoherePreTrainedModel", # noqa: F822
+]
diff --git a/third_party/transformers/src/transformers/models/cohere/tokenization_cohere.py b/third_party/transformers/src/transformers/models/cohere/tokenization_cohere.py
new file mode 100644
index 0000000000000000000000000000000000000000..fb25704360db9489b32443c6816a0dc7c441feeb
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/cohere/tokenization_cohere.py
@@ -0,0 +1,384 @@
+# Copyright 2024 Cohere team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This file is based on the tokenization_llama.py file in transformers
+
+from typing import Literal
+
+from tokenizers import Tokenizer, decoders, normalizers, pre_tokenizers
+from tokenizers.models import BPE
+
+from ...tokenization_utils_tokenizers import TokenizersBackend
+from ...utils import logging
+
+
+logger = logging.get_logger(__name__)
+VOCAB_FILES_NAMES = {"vocab_file": "vocab.json", "merges_file": "merges.txt", "tokenizer_file": "tokenizer.json"}
+
+PRETRAINED_VOCAB_FILES_MAP = {
+ "tokenizer_file": {
+ "Cohere/Command-nightly": "https://huggingface.co/Cohere/Command-nightly/blob/main/tokenizer.json",
+ },
+}
+
+# fmt: off
+DEFAULT_SYSTEM_PROMPT = "You are Command-R, a brilliant, sophisticated, AI-assistant trained to assist human users by providing thorough responses. You are trained by Cohere."
+DEFAULT_RAG_PREAMBLE = """## Task and Context
+You help people answer their questions and other requests interactively. You will be asked a very wide array of requests on all kinds of topics. You will be equipped with a wide range of search engines or similar tools to help you, which you use to research your answer. You should focus on serving the user's needs as best you can, which will be wide-ranging.
+
+## Style Guide
+Unless the user asks for a different style of answer, you should answer in full sentences, using proper grammar and spelling."""
+# fmt: on
+
+
+class CohereTokenizer(TokenizersBackend):
+ """
+ Construct a Cohere tokenizer. Based on byte-level Byte-Pair-Encoding.
+
+ This uses notably ByteFallback and NFC normalization.
+
+ ```python
+ >>> from transformers import AutoTokenizer
+
+ >>> tokenizer = AutoTokenizer.from_pretrained("CohereForAI/c4ai-command-r-v01")
+ >>> tokenizer.encode("Hello this is a test")
+ [5, 28339, 2075, 1801, 1671, 3282]
+ ```
+
+ If you want to change the `bos_token` or the `eos_token`, make sure to specify them when initializing the model, or
+ call `tokenizer.update_post_processor()` to make sure that the post-processing is correctly done (otherwise the
+ values of the first token and final token of an encoded sequence will not be correct). For more details, checkout
+ [post-processors] (https://huggingface.co/docs/tokenizers/api/post-processors) documentation.
+
+ You can get around that behavior by passing `add_prefix_space=True` when instantiating this tokenizer, but since
+ the model was not pretrained this way, it might yield a decrease in performance.
+
+
+
+ When used with `is_split_into_words=True`, this tokenizer needs to be instantiated with `add_prefix_space=True`.
+
+
+
+ This tokenizer inherits from [`TokenizersBackend`] which contains most of the main methods. Users should
+ refer to this superclass for more information regarding those methods.
+
+ Args:
+ vocab_file (`str`, *optional*):
+ Path to the vocabulary file.
+ merges_file (`str`, *optional*):
+ Path to the merges file.
+ tokenizer_file (`str`, *optional*):
+ [tokenizers](https://github.com/huggingface/tokenizers) file (generally has a .json extension) that
+ contains everything needed to load the tokenizer.
+ clean_up_tokenization_spaces (`bool`, *optional*, defaults to `False`):
+ Whether or not to cleanup spaces after decoding, cleanup consists in removing potential artifacts like
+ extra spaces.
+ unk_token (`str` or `tokenizers.AddedToken`, *optional*, defaults to `""`):
+ The unknown token. A token that is not in the vocabulary cannot be converted to an ID and is set to be this
+ token instead.
+ bos_token (`str` or `tokenizers.AddedToken`, *optional*, defaults to `""`):
+ The beginning of sequence token that was used during pretraining. Can be used a sequence classifier token.
+ eos_token (`str` or `tokenizers.AddedToken`, *optional*, defaults to `"<|END_OF_TURN_TOKEN|>"`):
+ The end of sequence token.
+ add_bos_token (`bool`, *optional*, defaults to `True`):
+ Whether or not to add an `bos_token` at the start of sequences.
+ add_eos_token (`bool`, *optional*, defaults to `False`):
+ Whether or not to add an `eos_token` at the end of sequences.
+ use_default_system_prompt (`bool`, *optional*, defaults to `False`):
+ Whether or not the default system prompt for Cohere tokenizer should be used.
+ add_prefix_space (`bool`, *optional*, defaults to `False`):
+ Whether or not the tokenizer should automatically add a prefix space
+ vocab (`str`, `dict` or `list`, *optional*):
+ Custom vocabulary dictionary. If not provided, vocabulary is loaded from vocab_file.
+ merges (`str` or `list[str]`, *optional*):
+ Custom merges list. If not provided, merges are loaded from `merges_file`.
+ """
+
+ vocab_files_names = VOCAB_FILES_NAMES
+ pretrained_vocab_files_map = PRETRAINED_VOCAB_FILES_MAP
+ padding_side = "left"
+ model_input_names = ["input_ids", "attention_mask"]
+ model = BPE
+ # No `max_model_input_sizes`
+
+ def __init__(
+ self,
+ vocab: str | dict[str, int] | None = None,
+ merges: str | list[str] | None = None,
+ errors: str = "replace",
+ unk_token: str = "",
+ bos_token: str = "",
+ eos_token: str = "<|END_OF_TURN_TOKEN|>",
+ pad_token: str = "",
+ cls_token: str = "",
+ sep_token: str = "",
+ mask_token: str = "",
+ use_default_system_prompt: bool = False,
+ add_prefix_space: bool = False,
+ **kwargs,
+ ):
+ self.use_default_system_prompt = use_default_system_prompt
+ self.add_prefix_space = add_prefix_space
+ self.grounded_generation_template = kwargs.pop("grounded_generation_template", None)
+ self.tool_use_template = kwargs.pop("tool_use_template", None)
+
+ self._vocab = (
+ vocab
+ if vocab is not None
+ else {
+ str(pad_token): 0,
+ str(unk_token): 1,
+ str(cls_token): 2,
+ str(sep_token): 3,
+ str(mask_token): 4,
+ str(bos_token): 5,
+ }
+ )
+
+ self._merges = merges or []
+ self._tokenizer = Tokenizer(
+ BPE(
+ vocab=self._vocab,
+ merges=self._merges,
+ dropout=None,
+ continuing_subword_prefix="",
+ end_of_word_suffix="",
+ fuse_unk=False,
+ )
+ )
+
+ self._tokenizer.normalizer = normalizers.NFC()
+ self._tokenizer.pre_tokenizer = pre_tokenizers.Sequence(
+ [
+ pre_tokenizers.Digits(individual_digits=True),
+ pre_tokenizers.ByteLevel(add_prefix_space=add_prefix_space, trim_offsets=True),
+ ]
+ )
+ self._tokenizer.decoder = decoders.ByteLevel(add_prefix_space=add_prefix_space, trim_offsets=True)
+
+ super().__init__(
+ errors=errors,
+ unk_token=unk_token,
+ bos_token=bos_token,
+ eos_token=eos_token,
+ pad_token=pad_token,
+ cls_token=cls_token,
+ sep_token=sep_token,
+ mask_token=mask_token,
+ use_default_system_prompt=use_default_system_prompt,
+ add_prefix_space=add_prefix_space,
+ **kwargs,
+ )
+
+ self._post_init()
+
+ def apply_tool_use_template(
+ self,
+ conversation: list[dict[str, str]],
+ tools: list[dict],
+ **kwargs,
+ ) -> str | list[int]:
+ """Create a Command-R tool-use prompt.
+
+ Once rendered, the prompt instructs the model to generate a list of actions to perform on a set of user supplied tools
+ to help carry out the user's requests.
+
+ Conceptually, this works in the same way as `apply_chat_format`, but takes an additional `tools` parameter.
+
+ Converts a chat in the form of a list of dictionaries with `"role"` and `"content"` keys and a list of available
+ tools for the model to use into a prompt string, or a list of token ids.
+ This method will use the tokenizer's `default_tool_use_template` template specified at the class level.
+ You can override the default template using the `tool_use_template` kwarg but the quality of your results may decrease.
+
+ Args:
+ conversation (list[dict[str, str]]): A list of dicts
+ with "role" and "content" keys, representing the chat history so far.
+ tools (list[Dict]): a list of tools to render into the prompt for the model to choose from.
+ See an example at the bottom of the docstring.
+ The format should be:
+ * name (str): The name of the tool to be called. Valid names contain only the characters a-z,
+ A-Z, 0-9, _ and must not begin with a digit.
+ * description (str): The description of what the tool does, the model uses the description to
+ choose when and how to call the function.
+ * parameter_definitions (list[Dict]): The input parameters of the tool. Accepts a dictionary
+ where the key is the name of the parameter and the value is the parameter spec.
+ Valid parameter names contain only the characters a-z, A-Z, 0-9, _ and must not begin with a digit.
+ Parameter specs are as follows:
+ * description (str): The description of the parameter.
+ * type (str): the type of the parameter - most effective for python builtin data types, such as 'str', 'bool'
+ * required: boolean: Denotes whether the parameter is always present (required) or not. Defaults to not required.
+ add_generation_prompt (bool, *optional*): Whether to end the prompt with the token(s) that indicate
+ the start of an assistant message. This is useful when you want to generate a response from the model.
+ Note that this argument will be passed to the chat template, and so it must be supported in the
+ template for this argument to have any effect.
+ tokenize (`bool`, defaults to `True`):
+ Whether to tokenize the output. If `False`, the output will be a string.
+ padding (`bool`, defaults to `False`):
+ Whether to pad sequences to the maximum length. Has no effect if tokenize is `False`.
+ truncation (`bool`, defaults to `False`):
+ Whether to truncate sequences at the maximum length. Has no effect if tokenize is `False`.
+ max_length (`int`, *optional*):
+ Maximum length (in tokens) to use for padding or truncation. Has no effect if tokenize is `False`. If
+ not specified, the tokenizer's `max_length` attribute will be used as a default.
+ return_tensors (`str` or [`~utils.TensorType`], *optional*):
+ If set, will return tensors of a particular framework. Has no effect if tokenize is `False`. Acceptable
+ values are:
+ - `'pt'`: Return PyTorch `torch.Tensor` objects.
+ - `'np'`: Return NumPy `np.ndarray` objects.
+ return_dict (`bool`, *optional*, defaults to `False`):
+ Whether to return a dictionary with named outputs. Has no effect if tokenize is `False`.
+ **tokenizer_kwargs: Additional kwargs to pass to the tokenizer.
+
+ Returns:
+ `str`: A rendered prompt string.
+ or if tokenize=True:
+ `list[int]`: A list of token ids representing the tokenized chat so far, including control tokens. This
+ output is ready to pass to the model, either directly or via methods like `generate()`.
+
+ Examples:
+
+ ```python
+ >> tokenizer = CohereTokenizer.from_pretrained("CohereForAI/c4ai-command-r-v01")
+ >> tools = [
+ {
+ "name": "internet_search",
+ "description": "Returns a list of relevant document snippets for a textual query retrieved from the internet",
+ "parameter_definitions": {
+ "query": {
+ "description": "Query to search the internet with",
+ "type": "str",
+ "required": True,
+ }
+ },
+ },
+ {
+ "name": "directly_answer",
+ "description": "Calls a standard (un-augmented) AI chatbot to generate a response given the conversation history",
+ "parameter_definitions": {},
+ },
+ ]
+ >> conversation = [
+ {"role": "user", "content": "Whats the biggest penguin in the world?"},
+ ]
+ >> # Render the prompt, ready for user to inspect, or for input into the model
+ >> prompt = tokenizer.apply_tool_use_template(conversation, tools=tools, tokenize=False, add_generation_prompt=True)
+ >> print(prompt)
+ >> inputs = tokenizer.encode(grounded_generation_prompt, add_special_tokens=False, return_tensors='pt')
+ >> outputs = model.generate(inputs, max_new_tokens=128)
+ >> print(tokenizer.decode(outputs[0]))
+ [
+ {
+ "tool_name": "internet_search",
+ "parameters": {
+ "query": "biggest penguin in the world"
+ }
+ }
+ ]
+ ```
+ """
+ return self.apply_chat_template(
+ conversation,
+ chat_template="tool_use",
+ tools=tools,
+ **kwargs,
+ )
+
+ def apply_grounded_generation_template(
+ self,
+ conversation: list[dict[str, str]],
+ documents: list[dict],
+ citation_mode: Literal["fast", "accurate"] = "accurate",
+ **kwargs,
+ ) -> str | list[int]:
+ """Create a Command-R grounded generation (aka RAG) prompt.
+
+ Once rendered, the prompt instructs the model to generate a response with citations in, based on supplied documents.
+
+ Conceptually, this works in the same way as `apply_chat_format`, but takes additional `documents`
+ and parameter `citation_mode` parameters.
+
+ Converts a list of dictionaries with `"role"` and `"content"` keys and a list of
+ documents for the model to ground its response on into a prompt string, or a list of token ids.
+ This method will use the tokenizer's `grounded_generation_template` template specified at the class level.
+ You can override the default template using the `grounded_generation_template` kwarg but the quality of your results may decrease.
+
+ Args:
+ conversation (list[dict[str, str]]): A list of dicts
+ with "role" and "content" keys, representing the chat history so far.
+ documents (list[dict[str, str]): A list of dicts, representing documents or tool outputs to ground your
+ generation on. A document is a semistructured dict, with a string to string mapping. Common fields are
+ `url`, `title`, `snippet` etc but should be descriptive of the key. They will get rendered into the prompt.
+ citation_mode: either "accurate" (prompt the model to generate an answer first, then rewrite it with citation
+ spans in) or "fast", where the prompt instructs the model to generate an answer with citations in directly.
+ The former has higher quality citations, the latter requires fewer tokens to be generated.
+ add_generation_prompt (bool, *optional*): Whether to end the prompt with the token(s) that indicate
+ the start of an assistant message. This is useful when you want to generate a response from the model.
+ Note that this argument will be passed to the chat template, and so it must be supported in the
+ template for this argument to have any effect.
+ tokenize (`bool`, defaults to `True`):
+ Whether to tokenize the output. If `False`, the output will be a string.
+ padding (`bool`, defaults to `False`):
+ Whether to pad sequences to the maximum length. Has no effect if tokenize is `False`.
+ truncation (`bool`, defaults to `False`):
+ Whether to truncate sequences at the maximum length. Has no effect if tokenize is `False`.
+ max_length (`int`, *optional*):
+ Maximum length (in tokens) to use for padding or truncation. Has no effect if tokenize is `False`. If
+ not specified, the tokenizer's `max_length` attribute will be used as a default.
+ return_tensors (`str` or [`~utils.TensorType`], *optional*):
+ If set, will return tensors of a particular framework. Has no effect if tokenize is `False`. Acceptable
+ values are:
+ - `'pt'`: Return PyTorch `torch.Tensor` objects.
+ - `'np'`: Return NumPy `np.ndarray` objects.
+ return_dict (`bool`, *optional*, defaults to `False`):
+ Whether to return a dictionary with named outputs. Has no effect if tokenize is `False`.
+ **tokenizer_kwargs: Additional kwargs to pass to the tokenizer.
+
+ Returns:
+ `str`: A rendered prompt string.
+ or if tokenize=True:
+ `list[int]`: A list of token ids representing the tokenized chat so far, including control tokens. This
+ output is ready to pass to the model, either directly or via methods like `generate()`.
+
+ Examples:
+
+ ```python
+ >> tokenizer = CohereTokenizer.from_pretrained('CohereForAI/c4ai-command-r-v01')
+
+ >> # define documents:
+ >> documents = [
+ { "title": "Tall penguins", "text": "Emperor penguins are the tallest." },
+ { "title": "Penguin habitats", "text": "Emperor penguins only live in Antarctica."}
+ ]
+ >> # define a conversation:
+ >> conversation = [
+ {"role": "user", "content": "Whats the biggest penguin in the world?"}
+ ]
+ >> # render the prompt, ready for user to inspect, or for input into the model:
+ >> grounded_generation_prompt = tokenizer.apply_grounded_generation_template(conversation, documents=documents, tokenize=False, add_generation_prompt=True)
+ >> print(grounded_generation_prompt)
+ >> inputs = tokenizer.encode(prompt, add_special_tokens=False, return_tensors='pt')
+ >> outputs = model.generate(inputs, max_new_tokens=128)
+ >> print(tokenizer.decode(outputs[0]))
+ ```
+ """
+ return self.apply_chat_template(
+ conversation,
+ chat_template="rag",
+ documents=documents,
+ citation_mode=citation_mode,
+ **kwargs,
+ )
+
+
+__all__ = ["CohereTokenizer"]
diff --git a/third_party/transformers/src/transformers/models/cohere2_vision/__init__.py b/third_party/transformers/src/transformers/models/cohere2_vision/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..cb545552edce5a1071f6903d18438b794796c74e
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/cohere2_vision/__init__.py
@@ -0,0 +1,29 @@
+# Copyright 2025 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_cohere2_vision import *
+ from .image_processing_cohere2_vision import *
+ from .modeling_cohere2_vision import *
+ from .processing_cohere2_vision import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/cohere2_vision/configuration_cohere2_vision.py b/third_party/transformers/src/transformers/models/cohere2_vision/configuration_cohere2_vision.py
new file mode 100644
index 0000000000000000000000000000000000000000..dbbe01ae7ed63f53e65c4830c0b65c911a67d17c
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/cohere2_vision/configuration_cohere2_vision.py
@@ -0,0 +1,65 @@
+# Copyright 2025 the Cohere Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...utils import auto_docstring
+from ..auto import CONFIG_MAPPING, AutoConfig
+
+
+@auto_docstring(checkpoint="CohereLabs/command-a-vision-07-2025")
+@strict
+class Cohere2VisionConfig(PreTrainedConfig):
+ r"""
+ downsample_factor (`int`, *optional*, defaults to 2):
+ The factor by which to downsample the input image.
+ alignment_intermediate_size (`int`, *optional*, defaults to 36864):
+ The size of the intermediate layer for alignment.
+ """
+
+ model_type = "cohere2_vision"
+ sub_configs = {"text_config": AutoConfig, "vision_config": AutoConfig}
+
+ vision_config: dict | PreTrainedConfig | None = None
+ text_config: dict | PreTrainedConfig | None = None
+ downsample_factor: int = 2
+ image_token_id: int = 255036
+ alignment_intermediate_size: int = 36864
+ tie_word_embeddings: bool = True
+
+ def __post_init__(self, **kwargs):
+ if isinstance(self.vision_config, dict):
+ self.vision_config["model_type"] = self.vision_config.get("model_type", "siglip_vision_model")
+ self.vision_config = CONFIG_MAPPING[self.vision_config["model_type"]](**self.vision_config)
+ elif self.vision_config is None:
+ self.vision_config = CONFIG_MAPPING["siglip_vision_model"](
+ hidden_size=1152,
+ intermediate_size=3072,
+ image_size=512,
+ num_hidden_layers=27,
+ num_attention_heads=12,
+ )
+
+ if isinstance(self.text_config, dict):
+ self.text_config["model_type"] = self.text_config.get("model_type", "cohere2")
+ self.text_config = CONFIG_MAPPING[self.text_config["model_type"]](**self.text_config)
+ elif self.text_config is None:
+ self.text_config = CONFIG_MAPPING["cohere2"](tie_word_embeddings=self.tie_word_embeddings)
+
+ super().__post_init__(**kwargs)
+
+
+__all__ = ["Cohere2VisionConfig"]
diff --git a/third_party/transformers/src/transformers/models/cohere2_vision/image_processing_cohere2_vision.py b/third_party/transformers/src/transformers/models/cohere2_vision/image_processing_cohere2_vision.py
new file mode 100644
index 0000000000000000000000000000000000000000..e21c5d83c05987234ec27aeb536922a2f0781749
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/cohere2_vision/image_processing_cohere2_vision.py
@@ -0,0 +1,293 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/cohere2_vision/modular_cohere2_vision.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_cohere2_vision.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2025 the Cohere Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from functools import lru_cache
+
+import numpy as np
+import torch
+from torchvision.transforms.v2 import functional as tvF
+
+from ...image_processing_backends import TorchvisionBackend
+from ...image_processing_utils import BatchFeature
+from ...image_transforms import group_images_by_shape, reorder_images
+from ...image_utils import OPENAI_CLIP_MEAN, OPENAI_CLIP_STD, PILImageResampling, SizeDict
+from ...processing_utils import ImagesKwargs, Unpack
+from ...utils import TensorType, auto_docstring
+
+
+class Cohere2VisionImageProcessorKwargs(ImagesKwargs, total=False):
+ r"""
+ crop_to_patches (`bool`, *optional*, defaults to `False`):
+ Whether to crop the image to patches. Can be overridden by the `crop_to_patches` parameter in the
+ `preprocess` method.
+ min_patches (`int`, *optional*, defaults to 1):
+ The minimum number of patches to be extracted from the image. Only has an effect if `crop_to_patches` is
+ set to `True`. Can be overridden by the `min_patches` parameter in the `preprocess` method.
+ max_patches (`int`, *optional*, defaults to 12):
+ The maximum number of patches to be extracted from the image. Only has an effect if `crop_to_patches` is
+ set to `True`. Can be overridden by the `max_patches` parameter in the `preprocess` method.
+ """
+
+ crop_to_patches: bool
+ min_patches: int
+ max_patches: int
+
+
+@lru_cache(maxsize=10)
+def get_all_supported_aspect_ratios(max_image_tiles: int) -> list[tuple[int, int]]:
+ """
+ Computes all allowed aspect ratios for a given maximum number of input tiles.
+
+ This function calculates all possible arrangements of tiles that can be formed
+ within the constraint of the maximum number of tiles. Each arrangement is
+ represented by its aspect ratio (width/height) and the corresponding tile configuration.
+
+ Args:
+ max_image_tiles (`int`):
+ The maximum number of tiles allowed.
+
+ Returns:
+ `list[tuple[int, int]]`: A list of tuples, each tuple representing a valid (width, height)
+ configuration in terms of number of tiles.
+
+ Example:
+ >>> get_all_supported_aspect_ratios(4)
+ [(1, 1), (1, 2), (1, 3), (1, 4), (2, 1), (2, 2), (3, 1), (4, 1)]
+
+ """
+ aspect_ratios = []
+ for width in range(1, max_image_tiles + 1):
+ for height in range(1, max_image_tiles + 1):
+ if width * height <= max_image_tiles:
+ aspect_ratios.append((width, height))
+ return aspect_ratios
+
+
+def get_optimal_tiled_canvas(
+ original_image_size: tuple[int, int],
+ target_tile_size: tuple[int, int],
+ min_image_tiles: int,
+ max_image_tiles: int,
+) -> tuple[int, int]:
+ possible_resolutions = get_all_supported_aspect_ratios(max_image_tiles)
+ possible_resolutions = sorted(possible_resolutions, key=lambda x: x[0] * x[1])
+ image_height, image_width = original_image_size
+ patch_size_height, patch_size_width = target_tile_size # (height == width)
+
+ candidate_resolutions = np.array(possible_resolutions) * patch_size_height
+ # tiles following (width, height) order to align with aspect ratio convention
+ tile_size = np.stack([image_width, image_height])
+ required_scales = candidate_resolutions / tile_size
+ required_scale = np.min(required_scales, axis=-1, keepdims=True) # [n_resolutions, 1]
+ if np.all(required_scale < 1):
+ # We are forced to downscale, so try to minimize the amount of downscaling
+ best_grid = possible_resolutions[np.argmax(required_scale)]
+ else:
+ # Pick the resolution that required the least upscaling so that it most closely fits the image
+ required_scale = np.where(required_scale < 1.0, 10e9, required_scale)
+ best_grid = possible_resolutions[np.argmin(required_scale)]
+ return best_grid # (width, height)
+
+
+@auto_docstring
+class Cohere2VisionImageProcessor(TorchvisionBackend):
+ valid_kwargs = Cohere2VisionImageProcessorKwargs
+ resample = PILImageResampling.BICUBIC
+ image_mean = OPENAI_CLIP_MEAN
+ image_std = OPENAI_CLIP_STD
+ size = {"height": 512, "width": 512}
+ do_resize = True
+ do_rescale = True
+ do_normalize = True
+ do_convert_rgb = True
+ crop_to_patches = True
+ min_patches = 1
+ max_patches = 12
+ patch_size = 16
+
+ def __init__(self, **kwargs: Unpack[Cohere2VisionImageProcessorKwargs]):
+ super().__init__(**kwargs)
+
+ def crop_image_to_patches(
+ self,
+ images: "torch.Tensor",
+ min_patches: int,
+ max_patches: int,
+ use_thumbnail: bool = True,
+ patch_size: SizeDict | None = None,
+ resample: "PILImageResampling | tvF.InterpolationMode | int | None" = None,
+ ):
+ """
+ Crop the images to patches and return a list of cropped images.
+ The number of patches and their grid arrangement are determined by the original image size,
+ the target patch size and the minimum and maximum number of patches.
+ The aspect ratio of the patches grid is chosen to be the closest to the original image aspect ratio.
+
+ Args:
+ images (`torch.Tensor`):
+ The images to be cropped.
+ min_patches (`int`):
+ The minimum number of patches to be extracted from the image.
+ max_patches (`int`):
+ The maximum number of patches to be extracted from the image.
+ use_thumbnail (`bool`, *optional*, defaults to `True`):
+ Whether to add a thumbnail image to the list of cropped patches.
+ patch_size (`SizeDict`, *optional*):
+ The size of the output patches.
+ resample (`PILImageResampling | tvF.InterpolationMode | int | None`, *optional*):
+ Resampling filter to use when resizing.
+ """
+ patch_size_height, patch_size_width = patch_size.height, patch_size.width
+ original_height, original_width = images.shape[-2:]
+ # find the closest aspect ratio to the target
+ num_columns, num_rows = get_optimal_tiled_canvas(
+ (original_height, original_width), (patch_size_height, patch_size_width), min_patches, max_patches
+ )
+
+ # calculate the target width and height
+ target_width = patch_size_width * num_columns
+ target_height = patch_size_height * num_rows
+ num_blocks = num_columns * num_rows
+
+ # resize the image so that each patch is of patch_size
+ resized_image = self.resize(images, SizeDict(height=target_height, width=target_width), resample=resample)
+ # split the image into patches
+ processed_images = []
+ for i in range(num_blocks):
+ column = i % num_columns
+ row = i // num_columns
+ box = (
+ column * patch_size_width,
+ row * patch_size_height,
+ (column + 1) * patch_size_width,
+ (row + 1) * patch_size_height,
+ )
+ # split the image
+ patch_image = resized_image[..., box[1] : box[3], box[0] : box[2]]
+ processed_images.append(patch_image)
+
+ if use_thumbnail and len(processed_images) != 1:
+ thumbnail_img = self.resize(images, patch_size, resample=resample)
+ processed_images.append(thumbnail_img)
+
+ processed_images = torch.stack(processed_images, dim=0).transpose(0, 1).contiguous()
+
+ return processed_images
+
+ def _preprocess(
+ self,
+ images: list["torch.Tensor"],
+ do_resize: bool,
+ size: SizeDict,
+ resample: "PILImageResampling | tvF.InterpolationMode | int | None",
+ do_rescale: bool,
+ rescale_factor: float,
+ do_normalize: bool,
+ image_mean: float | list[float] | None,
+ image_std: float | list[float] | None,
+ disable_grouping: bool | None,
+ return_tensors: str | TensorType | None,
+ crop_to_patches: bool = False,
+ min_patches: int = 1,
+ max_patches: int = 12,
+ **kwargs,
+ ) -> BatchFeature:
+ if crop_to_patches:
+ grouped_images, grouped_images_index = group_images_by_shape(images, disable_grouping=disable_grouping)
+ processed_images_grouped = {}
+ num_patches = {}
+ for shape, stacked_images in grouped_images.items():
+ stacked_images = self.crop_image_to_patches(
+ stacked_images,
+ min_patches,
+ max_patches,
+ patch_size=size,
+ resample=resample,
+ )
+ processed_images_grouped[shape] = stacked_images
+ num_patches[shape] = [stacked_images.shape[1]] * stacked_images.shape[0]
+ images = reorder_images(processed_images_grouped, grouped_images_index)
+ images = [image for images_list in images for image in images_list]
+ num_patches = reorder_images(num_patches, grouped_images_index)
+ else:
+ num_patches = [1] * len(images)
+
+ # Group images by size for batched resizing
+ grouped_images, grouped_images_index = group_images_by_shape(images, disable_grouping=disable_grouping)
+ resized_images_grouped = {}
+ for shape, stacked_images in grouped_images.items():
+ if do_resize:
+ stacked_images = self.resize(image=stacked_images, size=size, resample=resample)
+ resized_images_grouped[shape] = stacked_images
+ resized_images = reorder_images(resized_images_grouped, grouped_images_index)
+
+ # Group images by size for further processing
+ # Needed in case do_resize is False, or resize returns images with different sizes
+ grouped_images, grouped_images_index = group_images_by_shape(resized_images, disable_grouping=disable_grouping)
+ processed_images_grouped = {}
+ for shape, stacked_images in grouped_images.items():
+ stacked_images = self.rescale_and_normalize(
+ stacked_images, do_rescale, rescale_factor, do_normalize, image_mean, image_std
+ )
+ processed_images_grouped[shape] = stacked_images
+
+ processed_images = reorder_images(processed_images_grouped, grouped_images_index)
+
+ return BatchFeature(
+ data={"pixel_values": processed_images, "num_patches": num_patches}, tensor_type=return_tensors
+ )
+
+ def get_number_of_image_patches(self, height: int, width: int, images_kwargs=None):
+ """
+ A utility that returns number patches for a given image size.
+
+ Args:
+ height (`int`):
+ Height of the input image.
+ width (`int`):
+ Width of the input image.
+ images_kwargs (`dict`, *optional*)
+ Any kwargs to override defaults of the image processor.
+ Returns:
+ `int`: Number of patches per image.
+ """
+ min_patches = images_kwargs.get("min_patches", self.min_patches) if images_kwargs else self.min_patches
+ max_patches = images_kwargs.get("max_patches", self.max_patches) if images_kwargs else self.max_patches
+ patch_size = images_kwargs.get("patch_size", self.size) if images_kwargs else self.size
+ crop_to_patches = (
+ images_kwargs.get("crop_to_patches", self.crop_to_patches) if images_kwargs else self.crop_to_patches
+ )
+
+ num_patches = 1
+ if crop_to_patches and max_patches > 1:
+ if isinstance(patch_size, dict):
+ patch_height, patch_width = patch_size["height"], patch_size["width"]
+ else:
+ patch_height, patch_width = patch_size.height, patch_size.width
+ num_columns, num_rows = get_optimal_tiled_canvas(
+ (height, width), (patch_height, patch_width), min_patches, max_patches
+ )
+ if num_columns * num_rows > 1:
+ num_patches += num_columns * num_rows
+
+ return num_patches
+
+
+__all__ = ["Cohere2VisionImageProcessor"]
diff --git a/third_party/transformers/src/transformers/models/cohere2_vision/modeling_cohere2_vision.py b/third_party/transformers/src/transformers/models/cohere2_vision/modeling_cohere2_vision.py
new file mode 100644
index 0000000000000000000000000000000000000000..048412b383e7b141bd5b965bd6410892c4f754f4
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/cohere2_vision/modeling_cohere2_vision.py
@@ -0,0 +1,388 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/cohere2_vision/modular_cohere2_vision.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_cohere2_vision.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2025 the Cohere Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from dataclasses import dataclass
+
+import torch
+from torch import nn
+
+from ...cache_utils import Cache
+from ...generation import GenerationMixin
+from ...modeling_flash_attention_utils import FlashAttentionKwargs
+from ...modeling_outputs import BaseModelOutputWithPast, BaseModelOutputWithPooling, ModelOutput
+from ...modeling_utils import PreTrainedModel
+from ...processing_utils import Unpack
+from ...utils import TransformersKwargs, auto_docstring, can_return_tuple, torch_compilable_check
+from ..auto import AutoModel
+from .configuration_cohere2_vision import Cohere2VisionConfig
+
+
+class Cohere2VisionMultiModalProjector(nn.Module):
+ def __init__(self, config: Cohere2VisionConfig):
+ super().__init__()
+ self.config = config
+ self.downsample_factor = config.downsample_factor
+ self.intermediate_size = config.alignment_intermediate_size
+ self.linear_1 = nn.Linear(
+ config.vision_config.hidden_size * (config.downsample_factor**2), self.intermediate_size, bias=True
+ )
+ self.act = nn.SiLU()
+ self.linear_2 = nn.Linear(self.intermediate_size // 2, config.text_config.hidden_size, bias=True)
+
+ def pixel_shuffle(self, image_features): # B, S, D
+ batch_size, seq_length, feature_dim = image_features.shape
+ height = width = int(seq_length**0.5)
+ image_features = image_features.reshape(image_features.shape[0], width, height, -1)
+ channels = image_features.shape[-1]
+ image_features = image_features.reshape(
+ batch_size, width, int(height / self.downsample_factor), int(channels * self.downsample_factor)
+ )
+ image_features = image_features.permute(0, 2, 1, 3)
+ image_features = image_features.reshape(
+ batch_size, int(height / self.downsample_factor), int(width / self.downsample_factor), -1
+ )
+ image_features = image_features.permute(0, 2, 1, 3)
+ return image_features
+
+ def forward(self, image_features):
+ image_features = self.pixel_shuffle(image_features)
+ hidden_states = self.linear_1(image_features)
+
+ # Split along last dimension and apply SwiGLU
+ x, gate = hidden_states.chunk(2, dim=-1)
+ hidden_states = self.act(gate) * x
+
+ hidden_states = self.linear_2(hidden_states)
+ return hidden_states
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ Base class for Cohere2Vision outputs, with hidden states and attentions.
+ """
+)
+class Cohere2VisionModelOutputWithPast(BaseModelOutputWithPast):
+ r"""
+ past_key_values (`Cache`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
+ It is a [`~cache_utils.Cache`] instance. For more details, see our [kv cache guide](https://huggingface.co/docs/transformers/en/kv_cache).
+
+ Contains pre-computed hidden-states (key and values in the self-attention blocks) that can be used (see
+ `past_key_values` input) to speed up sequential decoding.
+ image_hidden_states (`torch.FloatTensor`, *optional*):
+ A `torch.FloatTensor` of size `(batch_size, num_images, sequence_length, hidden_size)`.
+ image_hidden_states of the model produced by the vision encoder and after projecting the last hidden state.
+ """
+
+ image_hidden_states: torch.FloatTensor | None = None
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ Base class for Cohere2Vision causal language model (or autoregressive) outputs.
+ """
+)
+class Cohere2VisionCausalLMOutputWithPast(ModelOutput):
+ r"""
+ loss (`torch.FloatTensor` of shape `(1,)`, *optional*, returned when `labels` is provided):
+ Language modeling loss (for next-token prediction).
+ logits (`torch.FloatTensor` of shape `(batch_size, sequence_length, config.vocab_size)`):
+ Prediction scores of the language modeling head (scores for each vocabulary token before SoftMax).
+ past_key_values (`Cache`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
+ It is a [`~cache_utils.Cache`] instance. For more details, see our [kv cache guide](https://huggingface.co/docs/transformers/en/kv_cache).
+
+ Contains pre-computed hidden-states (key and values in the self-attention blocks) that can be used (see
+ `past_key_values` input) to speed up sequential decoding.
+ image_hidden_states (`torch.FloatTensor`, *optional*):
+ A `torch.FloatTensor` of size `(batch_size, num_images, sequence_length, hidden_size)`.
+ image_hidden_states of the model produced by the vision encoder and after projecting the last hidden state.
+ """
+
+ loss: torch.FloatTensor | None = None
+ logits: torch.FloatTensor | None = None
+ past_key_values: Cache | None = None
+ hidden_states: tuple[torch.FloatTensor] | None = None
+ attentions: tuple[torch.FloatTensor] | None = None
+ image_hidden_states: torch.FloatTensor | None = None
+
+
+@auto_docstring
+class Cohere2VisionPreTrainedModel(PreTrainedModel):
+ config: Cohere2VisionConfig
+ base_model_prefix = "model"
+ input_modalities = ("image", "text")
+ supports_gradient_checkpointing = True
+ _skip_keys_device_placement = "past_key_values"
+
+ _supports_flash_attn = True
+ _supports_sdpa = True
+ _can_compile_fullgraph = False
+ _supports_flex_attn = True
+ _supports_attention_backend = True
+
+
+@auto_docstring(
+ custom_intro="""
+ The Cohere2Vision model which consists of a vision backbone and a language model, without a language modeling head.
+ """
+)
+class Cohere2VisionModel(Cohere2VisionPreTrainedModel):
+ def __init__(self, config: Cohere2VisionConfig):
+ super().__init__(config)
+ self.vision_tower = AutoModel.from_config(config.vision_config)
+
+ self.multi_modal_projector = Cohere2VisionMultiModalProjector(config)
+ self.language_model = AutoModel.from_config(config.text_config)
+ self.post_init()
+
+ def get_input_embeddings(self):
+ return self.language_model.get_input_embeddings()
+
+ def set_input_embeddings(self, value):
+ self.language_model.set_input_embeddings(value)
+
+ @can_return_tuple
+ @auto_docstring(
+ custom_intro="Obtains image last hidden states from the vision tower and apply multimodal projection."
+ )
+ def get_image_features(
+ self, pixel_values: torch.FloatTensor, **kwargs: Unpack[TransformersKwargs]
+ ) -> tuple | BaseModelOutputWithPooling:
+ image_outputs = self.vision_tower(pixel_values, return_dict=True, **kwargs)
+ selected_image_feature = image_outputs.last_hidden_state
+ image_outputs.pooler_output = self.multi_modal_projector(selected_image_feature)
+
+ return image_outputs
+
+ def get_placeholder_mask(
+ self, input_ids: torch.LongTensor, inputs_embeds: torch.FloatTensor, image_features: torch.FloatTensor
+ ):
+ """
+ Obtains multimodal placeholder mask from `input_ids` or `inputs_embeds`, and checks that the placeholder token count is
+ equal to the length of multimodal features. If the lengths are different, an error is raised.
+ """
+ if input_ids is None:
+ special_image_mask = inputs_embeds == self.get_input_embeddings()(
+ torch.tensor(self.config.image_token_id, dtype=torch.long, device=inputs_embeds.device)
+ )
+ special_image_mask = special_image_mask.all(-1)
+ else:
+ special_image_mask = input_ids == self.config.image_token_id
+
+ n_image_tokens = special_image_mask.sum()
+ n_image_features = image_features.shape[0] * image_features.shape[1]
+ special_image_mask = special_image_mask.unsqueeze(-1).expand_as(inputs_embeds).to(inputs_embeds.device)
+ torch_compilable_check(
+ inputs_embeds[special_image_mask].numel() == image_features.numel(),
+ f"Image features and image tokens do not match, tokens: {n_image_tokens}, features: {n_image_features}",
+ )
+ return special_image_mask
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ pixel_values: torch.FloatTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ use_cache: bool | None = None,
+ **kwargs: Unpack[FlashAttentionKwargs],
+ ) -> tuple | Cohere2VisionModelOutputWithPast:
+ 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 None:
+ inputs_embeds = self.get_input_embeddings()(input_ids)
+
+ if pixel_values is not None:
+ image_features = self.get_image_features(pixel_values, return_dict=True).pooler_output
+ image_features = image_features.to(inputs_embeds.device, inputs_embeds.dtype)
+ special_image_mask = self.get_placeholder_mask(
+ input_ids, inputs_embeds=inputs_embeds, image_features=image_features
+ )
+ inputs_embeds = inputs_embeds.masked_scatter(special_image_mask, image_features)
+
+ outputs = self.language_model(
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ return Cohere2VisionModelOutputWithPast(
+ last_hidden_state=outputs.last_hidden_state,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ image_hidden_states=image_features if pixel_values is not None else None,
+ )
+
+
+@auto_docstring(
+ custom_intro="""
+ The COHERE2_VISION model which consists of a vision backbone and a language model.
+ """
+)
+class Cohere2VisionForConditionalGeneration(Cohere2VisionPreTrainedModel, GenerationMixin):
+ _tied_weights_keys = {"lm_head.weight": "model.language_model.embed_tokens.weight"}
+
+ def __init__(self, config: Cohere2VisionConfig):
+ super().__init__(config)
+ self.model = Cohere2VisionModel(config)
+ self.lm_head = nn.Linear(config.text_config.hidden_size, config.text_config.vocab_size, bias=False)
+ self.post_init()
+
+ def get_input_embeddings(self):
+ return self.model.get_input_embeddings()
+
+ def set_input_embeddings(self, value):
+ self.model.set_input_embeddings(value)
+
+ def get_output_embeddings(self) -> nn.Module:
+ return self.lm_head
+
+ @auto_docstring
+ def get_image_features(
+ self, pixel_values: torch.FloatTensor, **kwargs: Unpack[TransformersKwargs]
+ ) -> tuple | BaseModelOutputWithPooling:
+ return self.model.get_image_features(pixel_values=pixel_values, **kwargs)
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ pixel_values: torch.FloatTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ image_sizes: torch.Tensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | Cohere2VisionCausalLMOutputWithPast:
+ r"""
+ 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]`.
+
+ Example:
+
+ ```python
+ >>> from transformers import AutoProcessor, Cohere2VisionForConditionalGeneration
+ >>> import torch
+
+ >>> processor = AutoProcessor.from_pretrained("CohereLabs/command-a-vision-07-2025", use_fast=True)
+ >>> model = Cohere2VisionForConditionalGeneration.from_pretrained("CohereLabs/command-a-vision-07-2025", device_map="auto")
+
+ >>> messages = [
+ ... {
+ ... "role": "user",
+ ... "content": [
+ ... {
+ ... "type": "image",
+ ... "url": "https://images.pexels.com/photos/1108099/pexels-photo-1108099.jpeg",
+ ... },
+ ... {"type": "text", "text": "what is in this image?"},
+ ... ],
+ ... },
+ ... ]
+
+ >>> inputs = processor.apply_chat_template(
+ ... messages, padding=True, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt",
+ ... ).to(model.device)
+
+ >>> gen_tokens = model.generate(**inputs, max_new_tokens=300, do_sample=True, temperature=0.3)
+ >>> processor.tokenizer.decode(gen_tokens[0][inputs.input_ids.shape[1]:], skip_special_tokens=True)
+ ```"""
+ outputs = self.model(
+ input_ids=input_ids,
+ pixel_values=pixel_values,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ image_sizes=image_sizes,
+ **kwargs,
+ )
+
+ hidden_states = outputs[0]
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(
+ logits=logits, labels=labels, vocab_size=self.config.text_config.vocab_size, **kwargs
+ )
+
+ return Cohere2VisionCausalLMOutputWithPast(
+ loss=loss,
+ logits=logits,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ image_hidden_states=outputs.image_hidden_states,
+ )
+
+ def prepare_inputs_for_generation(
+ self,
+ input_ids,
+ past_key_values=None,
+ inputs_embeds=None,
+ pixel_values=None,
+ attention_mask=None,
+ logits_to_keep=None,
+ is_first_iteration=False,
+ **kwargs,
+ ):
+ # Overwritten -- in specific circumstances we don't want to forward image inputs to the model
+
+ model_inputs = super().prepare_inputs_for_generation(
+ input_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ attention_mask=attention_mask,
+ logits_to_keep=logits_to_keep,
+ is_first_iteration=is_first_iteration,
+ **kwargs,
+ )
+
+ if is_first_iteration or not kwargs.get("use_cache", True):
+ # Pixel values are used only in the first iteration if available
+ # In subsequent iterations, they are already merged with text and cached
+ # NOTE: first iteration doesn't have to be prefill, it can be the first
+ # iteration with a question and cached system prompt (continue generate from cache)
+ model_inputs["pixel_values"] = pixel_values
+
+ return model_inputs
+
+
+__all__ = ["Cohere2VisionForConditionalGeneration", "Cohere2VisionPreTrainedModel", "Cohere2VisionModel"]
diff --git a/third_party/transformers/src/transformers/models/cohere2_vision/modular_cohere2_vision.py b/third_party/transformers/src/transformers/models/cohere2_vision/modular_cohere2_vision.py
new file mode 100644
index 0000000000000000000000000000000000000000..655b0983c79b5ca3b500daa27580aa351b433443
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/cohere2_vision/modular_cohere2_vision.py
@@ -0,0 +1,328 @@
+# Copyright 2025 the Cohere Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""PyTorch AyaVision model."""
+
+from functools import lru_cache
+
+import numpy as np
+import torch
+from torch import nn
+
+from transformers.models.aya_vision.modeling_aya_vision import (
+ AyaVisionCausalLMOutputWithPast,
+ AyaVisionForConditionalGeneration,
+ AyaVisionModel,
+ AyaVisionModelOutputWithPast,
+ AyaVisionPreTrainedModel,
+)
+from transformers.models.got_ocr2.image_processing_got_ocr2 import GotOcr2ImageProcessor
+
+from ...cache_utils import Cache
+from ...modeling_flash_attention_utils import FlashAttentionKwargs
+from ...modeling_outputs import BaseModelOutputWithPooling
+from ...processing_utils import ImagesKwargs, Unpack
+from ...utils import TransformersKwargs, auto_docstring, can_return_tuple, logging
+from .configuration_cohere2_vision import Cohere2VisionConfig
+
+
+logger = logging.get_logger(__name__)
+
+
+class Cohere2VisionMultiModalProjector(nn.Module):
+ def __init__(self, config: Cohere2VisionConfig):
+ super().__init__()
+ self.config = config
+ self.downsample_factor = config.downsample_factor
+ self.intermediate_size = config.alignment_intermediate_size
+ self.linear_1 = nn.Linear(
+ config.vision_config.hidden_size * (config.downsample_factor**2), self.intermediate_size, bias=True
+ )
+ self.act = nn.SiLU()
+ self.linear_2 = nn.Linear(self.intermediate_size // 2, config.text_config.hidden_size, bias=True)
+
+ def pixel_shuffle(self, image_features): # B, S, D
+ batch_size, seq_length, feature_dim = image_features.shape
+ height = width = int(seq_length**0.5)
+ image_features = image_features.reshape(image_features.shape[0], width, height, -1)
+ channels = image_features.shape[-1]
+ image_features = image_features.reshape(
+ batch_size, width, int(height / self.downsample_factor), int(channels * self.downsample_factor)
+ )
+ image_features = image_features.permute(0, 2, 1, 3)
+ image_features = image_features.reshape(
+ batch_size, int(height / self.downsample_factor), int(width / self.downsample_factor), -1
+ )
+ image_features = image_features.permute(0, 2, 1, 3)
+ return image_features
+
+ def forward(self, image_features):
+ image_features = self.pixel_shuffle(image_features)
+ hidden_states = self.linear_1(image_features)
+
+ # Split along last dimension and apply SwiGLU
+ x, gate = hidden_states.chunk(2, dim=-1)
+ hidden_states = self.act(gate) * x
+
+ hidden_states = self.linear_2(hidden_states)
+ return hidden_states
+
+
+class Cohere2VisionModelOutputWithPast(AyaVisionModelOutputWithPast):
+ pass
+
+
+class Cohere2VisionCausalLMOutputWithPast(AyaVisionCausalLMOutputWithPast):
+ pass
+
+
+class Cohere2VisionPreTrainedModel(AyaVisionPreTrainedModel):
+ base_model_prefix = "model"
+
+
+class Cohere2VisionModel(AyaVisionModel):
+ @can_return_tuple
+ @auto_docstring(
+ custom_intro="Obtains image last hidden states from the vision tower and apply multimodal projection."
+ )
+ def get_image_features(
+ self, pixel_values: torch.FloatTensor, **kwargs: Unpack[TransformersKwargs]
+ ) -> tuple | BaseModelOutputWithPooling:
+ image_outputs = self.vision_tower(pixel_values, return_dict=True, **kwargs)
+ selected_image_feature = image_outputs.last_hidden_state
+ image_outputs.pooler_output = self.multi_modal_projector(selected_image_feature)
+
+ return image_outputs
+
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ pixel_values: torch.FloatTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ use_cache: bool | None = None,
+ **kwargs: Unpack[FlashAttentionKwargs],
+ ) -> tuple | Cohere2VisionModelOutputWithPast:
+ 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 None:
+ inputs_embeds = self.get_input_embeddings()(input_ids)
+
+ if pixel_values is not None:
+ image_features = self.get_image_features(pixel_values, return_dict=True).pooler_output
+ image_features = image_features.to(inputs_embeds.device, inputs_embeds.dtype)
+ special_image_mask = self.get_placeholder_mask(
+ input_ids, inputs_embeds=inputs_embeds, image_features=image_features
+ )
+ inputs_embeds = inputs_embeds.masked_scatter(special_image_mask, image_features)
+
+ outputs = self.language_model(
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ return Cohere2VisionModelOutputWithPast(
+ last_hidden_state=outputs.last_hidden_state,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ image_hidden_states=image_features if pixel_values is not None else None,
+ )
+
+
+class Cohere2VisionForConditionalGeneration(AyaVisionForConditionalGeneration):
+ @auto_docstring
+ def get_image_features(
+ self, pixel_values: torch.FloatTensor, **kwargs: Unpack[TransformersKwargs]
+ ) -> tuple | BaseModelOutputWithPooling:
+ return self.model.get_image_features(pixel_values=pixel_values, **kwargs)
+
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ pixel_values: torch.FloatTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ image_sizes: torch.Tensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | Cohere2VisionCausalLMOutputWithPast:
+ r"""
+ 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]`.
+
+ Example:
+
+ ```python
+ >>> from transformers import AutoProcessor, Cohere2VisionForConditionalGeneration
+ >>> import torch
+
+ >>> processor = AutoProcessor.from_pretrained("CohereLabs/command-a-vision-07-2025", use_fast=True)
+ >>> model = Cohere2VisionForConditionalGeneration.from_pretrained("CohereLabs/command-a-vision-07-2025", device_map="auto")
+
+ >>> messages = [
+ ... {
+ ... "role": "user",
+ ... "content": [
+ ... {
+ ... "type": "image",
+ ... "url": "https://images.pexels.com/photos/1108099/pexels-photo-1108099.jpeg",
+ ... },
+ ... {"type": "text", "text": "what is in this image?"},
+ ... ],
+ ... },
+ ... ]
+
+ >>> inputs = processor.apply_chat_template(
+ ... messages, padding=True, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt",
+ ... ).to(model.device)
+
+ >>> gen_tokens = model.generate(**inputs, max_new_tokens=300, do_sample=True, temperature=0.3)
+ >>> processor.tokenizer.decode(gen_tokens[0][inputs.input_ids.shape[1]:], skip_special_tokens=True)
+ ```"""
+ outputs = self.model(
+ input_ids=input_ids,
+ pixel_values=pixel_values,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ image_sizes=image_sizes,
+ **kwargs,
+ )
+
+ hidden_states = outputs[0]
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(
+ logits=logits, labels=labels, vocab_size=self.config.text_config.vocab_size, **kwargs
+ )
+
+ return Cohere2VisionCausalLMOutputWithPast(
+ loss=loss,
+ logits=logits,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ image_hidden_states=outputs.image_hidden_states,
+ )
+
+
+@lru_cache(maxsize=10)
+def get_all_supported_aspect_ratios(max_image_tiles: int) -> list[tuple[int, int]]:
+ """
+ Computes all allowed aspect ratios for a given maximum number of input tiles.
+
+ This function calculates all possible arrangements of tiles that can be formed
+ within the constraint of the maximum number of tiles. Each arrangement is
+ represented by its aspect ratio (width/height) and the corresponding tile configuration.
+
+ Args:
+ max_image_tiles (`int`):
+ The maximum number of tiles allowed.
+
+ Returns:
+ `list[tuple[int, int]]`: A list of tuples, each tuple representing a valid (width, height)
+ configuration in terms of number of tiles.
+
+ Example:
+ >>> get_all_supported_aspect_ratios(4)
+ [(1, 1), (1, 2), (1, 3), (1, 4), (2, 1), (2, 2), (3, 1), (4, 1)]
+
+ """
+ aspect_ratios = []
+ for width in range(1, max_image_tiles + 1):
+ for height in range(1, max_image_tiles + 1):
+ if width * height <= max_image_tiles:
+ aspect_ratios.append((width, height))
+ return aspect_ratios
+
+
+def get_optimal_tiled_canvas(
+ original_image_size: tuple[int, int],
+ target_tile_size: tuple[int, int],
+ min_image_tiles: int,
+ max_image_tiles: int,
+) -> tuple[int, int]:
+ possible_resolutions = get_all_supported_aspect_ratios(max_image_tiles)
+ possible_resolutions = sorted(possible_resolutions, key=lambda x: x[0] * x[1])
+ image_height, image_width = original_image_size
+ patch_size_height, patch_size_width = target_tile_size # (height == width)
+
+ candidate_resolutions = np.array(possible_resolutions) * patch_size_height
+ # tiles following (width, height) order to align with aspect ratio convention
+ tile_size = np.stack([image_width, image_height])
+ required_scales = candidate_resolutions / tile_size
+ required_scale = np.min(required_scales, axis=-1, keepdims=True) # [n_resolutions, 1]
+ if np.all(required_scale < 1):
+ # We are forced to downscale, so try to minimize the amount of downscaling
+ best_grid = possible_resolutions[np.argmax(required_scale)]
+ else:
+ # Pick the resolution that required the least upscaling so that it most closely fits the image
+ required_scale = np.where(required_scale < 1.0, 10e9, required_scale)
+ best_grid = possible_resolutions[np.argmin(required_scale)]
+ return best_grid # (width, height)
+
+
+class Cohere2VisionImageProcessorKwargs(ImagesKwargs, total=False):
+ r"""
+ crop_to_patches (`bool`, *optional*, defaults to `False`):
+ Whether to crop the image to patches. Can be overridden by the `crop_to_patches` parameter in the
+ `preprocess` method.
+ min_patches (`int`, *optional*, defaults to 1):
+ The minimum number of patches to be extracted from the image. Only has an effect if `crop_to_patches` is
+ set to `True`. Can be overridden by the `min_patches` parameter in the `preprocess` method.
+ max_patches (`int`, *optional*, defaults to 12):
+ The maximum number of patches to be extracted from the image. Only has an effect if `crop_to_patches` is
+ set to `True`. Can be overridden by the `max_patches` parameter in the `preprocess` method.
+ """
+
+ crop_to_patches: bool
+ min_patches: int
+ max_patches: int
+
+
+@auto_docstring
+class Cohere2VisionImageProcessor(GotOcr2ImageProcessor):
+ size = {"height": 512, "width": 512}
+ min_patches = 1
+ max_patches = 12
+ crop_to_patches = True
+ patch_size = 16
+ valid_kwargs = Cohere2VisionImageProcessorKwargs
+
+
+__all__ = [
+ "Cohere2VisionForConditionalGeneration",
+ "Cohere2VisionPreTrainedModel",
+ "Cohere2VisionModel",
+ "Cohere2VisionImageProcessor",
+]
diff --git a/third_party/transformers/src/transformers/models/cohere2_vision/processing_cohere2_vision.py b/third_party/transformers/src/transformers/models/cohere2_vision/processing_cohere2_vision.py
new file mode 100644
index 0000000000000000000000000000000000000000..7d76f1187733020444c8c4b6b12748ed4b23c47f
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/cohere2_vision/processing_cohere2_vision.py
@@ -0,0 +1,169 @@
+# Copyright 2025 HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+from ...image_processing_utils import BatchFeature
+from ...image_utils import ImageInput
+from ...processing_utils import MultiModalData, ProcessingKwargs, ProcessorMixin, Unpack
+from ...tokenization_utils_base import PreTokenizedInput, TextInput
+from ...utils import auto_docstring
+
+
+class Cohere2VisionProcessorKwargs(ProcessingKwargs, total=False):
+ _defaults = {
+ "text_kwargs": {
+ "padding_side": "left",
+ "padding": True,
+ "return_mm_token_type_ids": False,
+ },
+ }
+
+
+@auto_docstring
+class Cohere2VisionProcessor(ProcessorMixin):
+ def __init__(
+ self,
+ image_processor=None,
+ tokenizer=None,
+ chat_template=None,
+ **kwargs,
+ ):
+ super().__init__(image_processor, tokenizer, chat_template=chat_template)
+
+ self.patch_size = self.image_processor.patch_size
+ self.boi_token = tokenizer.boi_token
+ self.eoi_token = tokenizer.eoi_token
+ self.image_token = tokenizer.image_token
+ self.img_line_break_token = tokenizer.img_line_break_token
+ self.image_token_id = tokenizer.image_token_id
+
+ self.image_ids = tokenizer.convert_tokens_to_ids(
+ [
+ self.image_token,
+ self.boi_token,
+ self.eoi_token,
+ self.img_line_break_token,
+ ]
+ )
+
+ @auto_docstring
+ def __call__(
+ self,
+ images: ImageInput | None = None,
+ text: TextInput | PreTokenizedInput | list[TextInput] | list[PreTokenizedInput] | None = None,
+ **kwargs: Unpack[Cohere2VisionProcessorKwargs],
+ ) -> BatchFeature:
+ r"""
+ Returns:
+ [`BatchFeature`]: A [`BatchFeature`] with the following fields:
+
+ - **input_ids** -- List of token ids to be fed to a model. Returned when `text` is not `None`.
+ - **attention_mask** -- List of indices specifying which tokens should be attended to by the model (when
+ `return_attention_mask=True` or if *"attention_mask"* is in `self.model_input_names` and if `text` is not
+ `None`).
+ - **pixel_values** -- Pixel values to be fed to a model. Returned when `images` is not `None`.
+ """
+ if text is None:
+ raise ValueError("You have to specify text.")
+ elif not isinstance(text, (list, tuple)):
+ text = [text]
+
+ output_kwargs = self._merge_kwargs(
+ Cohere2VisionProcessorKwargs,
+ tokenizer_init_kwargs=self.tokenizer.init_kwargs,
+ **kwargs,
+ )
+
+ # Process images
+ image_inputs = {}
+ if images is not None:
+ image_inputs = self.image_processor(images=images, **output_kwargs["images_kwargs"])
+ batch_num_patches = iter(image_inputs.pop("num_patches"))
+ processed_text = []
+ for sample in text:
+ while self.image_token in sample:
+ num_patches = next(batch_num_patches)
+ img_patches_per_tile = int(self.patch_size**2)
+
+ img_string = f"{self.boi_token}"
+ for idx in range(1, num_patches):
+ img_string += "" * img_patches_per_tile + self.img_line_break_token
+ img_string += "" * img_patches_per_tile + self.img_line_break_token
+ img_string += f"{self.eoi_token}"
+
+ sample = sample.replace(self.image_token, img_string, 1)
+ processed_text.append(sample)
+ text = [sample.replace("", self.image_token) for sample in processed_text]
+
+ return_tensors = output_kwargs["text_kwargs"].pop("return_tensors", None)
+ return_mm_token_type_ids = output_kwargs["text_kwargs"].pop("return_mm_token_type_ids", False)
+ text_inputs = self.tokenizer(text, **output_kwargs["text_kwargs"], return_tensors=None)
+
+ if return_mm_token_type_ids:
+ text_inputs["mm_token_type_ids"] = self.create_mm_token_type_ids(text_inputs["input_ids"])
+ return BatchFeature(data={**text_inputs, **image_inputs}, tensor_type=return_tensors)
+
+ def _get_num_multimodal_tokens(self, image_sizes=None, **kwargs):
+ """
+ Computes the number of placeholder tokens needed for multimodal inputs with the given sizes.
+
+ Args:
+ image_sizes (`list[list[int]]`, *optional*):
+ The input sizes formatted as (height, width) per each image.
+
+ Returns:
+ `MultiModalData`: A `MultiModalData` object holding number of tokens per each of the provided
+ input modalities, along with other useful data.
+ """
+
+ vision_data = {}
+ if image_sizes is not None:
+ images_kwargs = Cohere2VisionProcessorKwargs._defaults.get("images_kwargs", {})
+ images_kwargs.update(kwargs)
+
+ num_image_patches = [
+ self.image_processor.get_number_of_image_patches(*image_size, images_kwargs)
+ for image_size in image_sizes
+ ]
+
+ token_per_patch = int(self.patch_size**2)
+ num_image_tokens = [
+ 2 + sum(token_per_patch + 1 for _ in range(num_patches)) for num_patches in num_image_patches
+ ] # Add +2 and +1 for BOI/EOI and image break tokens
+ vision_data.update({"num_image_tokens": num_image_tokens, "num_image_patches": num_image_patches})
+
+ return MultiModalData(**vision_data)
+
+ def batch_decode(self, *args, **kwargs):
+ """
+ This method forwards all its arguments to PreTrainedTokenizerFast's [`~PreTrainedTokenizer.batch_decode`]. Please
+ refer to the docstring of this method for more information.
+ """
+ return self.tokenizer.batch_decode(*args, **kwargs)
+
+ def decode(self, *args, **kwargs):
+ """
+ This method forwards all its arguments to PreTrainedTokenizerFast's [`~PreTrainedTokenizer.decode`]. Please refer to
+ the docstring of this method for more information.
+ """
+ return self.tokenizer.decode(*args, **kwargs)
+
+ @property
+ def model_input_names(self):
+ tokenizer_input_names = self.tokenizer.model_input_names
+ image_processor_input_names = self.image_processor.model_input_names
+ return list(tokenizer_input_names) + list(image_processor_input_names)
+
+
+__all__ = ["Cohere2VisionProcessor"]
diff --git a/third_party/transformers/src/transformers/models/csm/__init__.py b/third_party/transformers/src/transformers/models/csm/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..59468442b52eb71fbcb984c28bb465cec2be91e5
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/csm/__init__.py
@@ -0,0 +1,28 @@
+# Copyright 2025 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_csm import *
+ from .modeling_csm import *
+ from .processing_csm import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/csm/configuration_csm.py b/third_party/transformers/src/transformers/models/csm/configuration_csm.py
new file mode 100644
index 0000000000000000000000000000000000000000..66784bac2085645c42e369dd2f93aa6336cc124b
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/csm/configuration_csm.py
@@ -0,0 +1,189 @@
+# Copyright 2025 Sesame and The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...modeling_rope_utils import RopeParameters
+from ...utils import auto_docstring, logging
+from ..auto.configuration_auto import AutoConfig
+
+
+logger = logging.get_logger(__name__)
+
+
+@auto_docstring(checkpoint="sesame/csm-1b")
+@strict
+class CsmDepthDecoderConfig(PreTrainedConfig):
+ r"""
+ backbone_hidden_size (`int`, *optional*, defaults to 2048):
+ Dimension of the hidden representations of the backbone model used with this depth decoder.
+
+ Example:
+
+ ```python
+ >>> from transformers import CsmDepthDecoder, CsmDepthDecoderConfig
+
+ >>> # Initializing a CsmDepthDecoder
+ >>> configuration = CsmDepthDecoderConfig()
+ >>> model = CsmDepthDecoderModel(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "csm_depth_decoder_model"
+ base_config_key = "depth_decoder_config"
+ keys_to_ignore_at_inference = ["past_key_values"]
+ attribute_map = {
+ "codebook_size": "vocab_size",
+ }
+ default_theta = 500000.0
+
+ num_codebooks: int | None = 32
+ backbone_hidden_size: int = 2048
+ vocab_size: int = 2051
+ hidden_size: int = 1024
+ intermediate_size: int = 8192
+ num_hidden_layers: int = 4
+ num_attention_heads: int = 8
+ num_key_value_heads: int | None = 2
+ hidden_act: str = "silu"
+ max_position_embeddings: int = 33
+ initializer_range: float = 0.02
+ rms_norm_eps: float = 1e-5
+ use_cache: bool = True
+ pad_token_id: int | None = None
+ bos_token_id: int | None = None
+ eos_token_id: int | list[int] | None = None
+ rope_parameters: RopeParameters | dict | None = None
+ attention_bias: bool = False
+ attention_dropout: float | int | None = 0.0
+ mlp_bias: bool = False
+ head_dim: int | None = None
+
+ def __post_init__(self, **kwargs):
+ if kwargs.pop("tie_word_embeddings", False):
+ raise ValueError("`tie_word_embeddings=True` is not supported for CsmDepthDecoderConfig")
+
+ # for backward compatibility
+ if self.num_key_value_heads is None:
+ self.num_key_value_heads = self.num_attention_heads
+ self.head_dim = self.head_dim if self.head_dim is not None else self.hidden_size // self.num_attention_heads
+ super().__post_init__(**kwargs)
+
+
+@auto_docstring(checkpoint="sesame/csm-1b")
+@strict
+class CsmConfig(PreTrainedConfig):
+ r"""
+ codebook_pad_token_id (`int`, *optional*, defaults to 2050):
+ Padding token id for codebook tokens.
+ codebook_eos_token_id (`int`, *optional*, defaults to 0):
+ End of stream token id for codebook tokens.
+ audio_token_id (`int`, *optional*, defaults to 128002):
+ Audio token id in the text input.
+ audio_eos_token_id (`int`, *optional*, defaults to 128003):
+ End of stream token id for audio in the text input.
+ tie_codebooks_embeddings (`bool`, *optional*, defaults to `True`):
+ Whether to tie the codebook tokens embeddings of the backbone model to the codebook tokens embeddings of the depth decoder.
+ depth_decoder_config (`CsmDepthDecoderConfig`, *optional*):
+ Configuration for the depth decoder.
+ codec_config (`PreTrainedConfig`, *optional*):
+ Configuration for the codec.
+
+ ```python
+ >>> from transformers import CsmForConditionalGeneration, CsmConfig
+
+ >>> # Initializing a CsmConfig
+ >>> configuration = CsmConfig()
+
+ >>> # Initializing a model
+ >>> model = CsmForConditionalGeneration(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```
+ """
+
+ model_type = "csm"
+ base_config_key = "csm_config"
+ keys_to_ignore_at_inference = ["past_key_values"]
+ default_theta = 500000.0
+ sub_configs = {
+ "codec_config": AutoConfig,
+ "depth_decoder_config": CsmDepthDecoderConfig,
+ }
+ attribute_map = {
+ "codebook_size": "vocab_size",
+ }
+
+ num_codebooks: int | None = 32
+ vocab_size: int = 2051
+ text_vocab_size: int = 128256
+ hidden_size: int = 2048
+ intermediate_size: int = 8192
+ num_hidden_layers: int = 16
+ num_attention_heads: int = 32
+ num_key_value_heads: int | None = 8
+ hidden_act: str = "silu"
+ max_position_embeddings: int = 2048
+ initializer_range: float = 0.02
+ rms_norm_eps: float = 1e-5
+ use_cache: bool = True
+ pad_token_id: int | None = 128002
+ codebook_pad_token_id: int | None = 2050
+ codebook_eos_token_id: int | list[int] | None = 0
+ bos_token_id: int | None = 128000
+ eos_token_id: int | list[int] | None = None
+ audio_token_id: int | None = 128002
+ audio_eos_token_id: int | list[int] | None = 128003
+ rope_parameters: RopeParameters | dict | None = None
+ attention_bias: bool = False
+ attention_dropout: float | int | None = 0.0
+ mlp_bias: bool = False
+ head_dim: int | None = None
+ tie_codebooks_embeddings: bool | None = True
+ depth_decoder_config: dict | PreTrainedConfig | None = None
+ codec_config: dict | PreTrainedConfig | None = None
+
+ def __post_init__(self, **kwargs):
+ if kwargs.pop("tie_word_embeddings", False):
+ raise ValueError("`tie_word_embeddings=True` is not supported for CsmConfig")
+
+ if self.depth_decoder_config is None:
+ self.depth_decoder_config = CsmDepthDecoderConfig()
+ logger.info("depth_decoder_config is None, using default depth decoder config.")
+ elif isinstance(self.depth_decoder_config, dict):
+ self.depth_decoder_config = CsmDepthDecoderConfig(**self.depth_decoder_config)
+
+ if self.codec_config is None:
+ self.codec_config = AutoConfig.for_model("mimi")
+ logger.info("codec_config is None, using default audio encoder config.")
+ elif isinstance(self.codec_config, dict):
+ self.codec_config = AutoConfig.for_model(**self.codec_config)
+
+ if self.num_key_value_heads is None:
+ self.num_key_value_heads = self.num_attention_heads
+
+ self.head_dim = self.head_dim if self.head_dim is not None else self.hidden_size // self.num_attention_heads
+ self.tie_word_embeddings = False
+ super().__post_init__(**kwargs)
+
+
+__all__ = [
+ "CsmDepthDecoderConfig",
+ "CsmConfig",
+]
diff --git a/third_party/transformers/src/transformers/models/csm/convert_csm.py b/third_party/transformers/src/transformers/models/csm/convert_csm.py
new file mode 100644
index 0000000000000000000000000000000000000000..e7c72f84c1ca50cf23f8e3832a10681be9d8bb9e
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/csm/convert_csm.py
@@ -0,0 +1,333 @@
+# Copyright 2025 The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import gc
+import os
+import re
+
+import torch
+from tokenizers.processors import TemplateProcessing
+
+from transformers import (
+ AutoFeatureExtractor,
+ AutoTokenizer,
+ CsmConfig,
+ CsmDepthDecoderConfig,
+ CsmForConditionalGeneration,
+ CsmProcessor,
+ MimiModel,
+)
+from transformers.utils.hub import cached_file
+
+
+# fmt: off
+ORIGINAL_TO_CONVERTED_KEY_MAPPING = {
+ r"backbone\.layers\.(\d+)": r"backbone_model.layers.\1",
+ r"decoder\.layers\.(\d+)": r"depth_decoder.model.layers.\1",
+
+ r"attn": r"self_attn",
+ r"output_proj": r"o_proj",
+ r"w1": r"gate_proj",
+ r"w2": r"down_proj",
+ r"w3": r"up_proj",
+
+ r"text_embeddings": r"embed_text_tokens",
+ r"audio_embeddings": r"backbone_model.embed_tokens.embed_audio_tokens",
+
+ r"codebook0_head": r"lm_head",
+ r"audio_head": r"depth_decoder.codebooks_head.weight",
+ r"projection": r"depth_decoder.model.inputs_embeds_projector",
+
+ r"sa_norm.scale": r"input_layernorm.weight",
+ r"mlp_norm.scale": r"post_attention_layernorm.weight",
+ r"decoder.norm.scale": r"depth_decoder.model.norm.weight",
+ r"backbone.norm.scale": r"backbone_model.norm.weight",
+}
+# fmt: on
+
+
+def permute_for_rope(input_tensor, n_heads, dim1, dim2):
+ """
+ When you go from the complex ROPE formulation to sin and cos one, you need
+ to permute the query and key weights (to avoid doing it on the fly)
+ """
+ input_tensor = input_tensor.reshape(dim1, dim2)
+ input_tensor = input_tensor.view(n_heads, dim1 // n_heads // 2, 2, dim2)
+ input_tensor = input_tensor.transpose(1, 2).reshape(dim1, dim2)
+ return input_tensor
+
+
+def convert_key(key, mapping):
+ for pattern, replacement in mapping.items():
+ key = re.sub(pattern, replacement, key)
+ return key
+
+
+def write_model(
+ input_path_or_repo,
+ model_name,
+ codec_model_path_or_repo,
+ output_dir,
+):
+ print("Converting the model.")
+ os.makedirs(output_dir, exist_ok=True)
+
+ codec_model = MimiModel.from_pretrained(codec_model_path_or_repo)
+ codec_model.config._attn_implementation_autoset = False
+
+ # prepare rope scaling args: the model uses originally
+ # 1 - for the depth decoder
+ # rope_theta=500000,
+ # rope_parameters={
+ # "factor": 32.0,
+ # "high_freq_factor": 4.0,
+ # "low_freq_factor": 1.0,
+ # "original_max_position_embeddings": 8192,
+ # "rope_type": "llama3",
+ # },
+ # 2 - for the backbone
+ # rope_theta=500000,
+ # rope_parameters={
+ # "factor": 32.0,
+ # "high_freq_factor": 4.0,
+ # "low_freq_factor": 1.0,
+ # "original_max_position_embeddings": 8192,
+ # "rope_type": "llama3",
+ # },
+ #
+ # Yet we want to use max_position_embeddings=32, resp. 2048
+ # This will throw warning as we would have original_max_position_embeddings >= max_position_embeddings
+ # Therefore, we convert values to equivalent ones
+
+ depth_decoder_config = CsmDepthDecoderConfig(
+ rope_parameters={
+ "factor": 32.0,
+ "high_freq_factor": 0.0078125,
+ "low_freq_factor": 0.001953125,
+ "original_max_position_embeddings": 16,
+ "rope_type": "llama3",
+ },
+ )
+
+ config = CsmConfig(
+ codec_config=codec_model.config,
+ depth_decoder_config=depth_decoder_config,
+ rope_parameters={
+ "factor": 32.0,
+ "high_freq_factor": 0.5,
+ "low_freq_factor": 0.125,
+ "original_max_position_embeddings": 1024,
+ "rope_type": "llama3",
+ },
+ )
+
+ params = {
+ "backbone": {
+ "num_attention_heads": config.num_attention_heads,
+ "num_key_value_heads": config.num_key_value_heads,
+ "dim_per_head": config.head_dim,
+ "key_value_dim": config.head_dim * config.num_key_value_heads,
+ "dim": config.hidden_size,
+ },
+ "depth_decoder": {
+ "num_attention_heads": config.depth_decoder_config.num_attention_heads,
+ "num_key_value_heads": config.depth_decoder_config.num_key_value_heads,
+ "dim_per_head": config.depth_decoder_config.head_dim,
+ "key_value_dim": config.depth_decoder_config.head_dim * config.depth_decoder_config.num_key_value_heads,
+ "dim": config.depth_decoder_config.hidden_size,
+ },
+ }
+
+ model_path = cached_file(
+ input_path_or_repo,
+ model_name,
+ )
+ print(f"Fetching all parameters from the checkpoint at {model_path}...")
+ loaded = torch.load(model_path, map_location="cpu")
+
+ print("Converting model...")
+ state_dict = {}
+
+ # -----------------------
+ # convert parameter names
+ # -----------------------
+
+ # Add codec_model. prefix to every key in the codec model state dict
+ codec_state_dict = {f"codec_model.{k}": v for k, v in codec_model.state_dict().items()}
+ state_dict.update(codec_state_dict)
+
+ for key, value in loaded.items():
+ new_key = convert_key(key, ORIGINAL_TO_CONVERTED_KEY_MAPPING)
+ current_parameter = value
+
+ # Post-process the current_parameter.
+ if re.search("(k|q)_proj.weight", new_key):
+ params_keys = "backbone" if "backbone" in new_key else "depth_decoder"
+ if "q_proj" in new_key:
+ num_heads = params[params_keys]["num_attention_heads"]
+ dim_per_head = params[params_keys]["dim_per_head"]
+ param_dim = params[params_keys]["dim"]
+ dim = params[params_keys]["dim"]
+ else:
+ num_heads = params[params_keys]["num_key_value_heads"]
+ dim_per_head = params[params_keys]["dim_per_head"]
+ param_dim = params[params_keys]["key_value_dim"]
+ dim = params[params_keys]["dim"]
+
+ current_parameter = permute_for_rope(value, num_heads, param_dim, dim)
+ state_dict[new_key] = current_parameter.reshape(num_heads * dim_per_head, dim)
+
+ state_dict[new_key] = current_parameter
+
+ # add the depth decoder embed audio tokens weights, latter tied to the backbone embed audio tokens weights
+ state_dict["depth_decoder.model.embed_tokens.weight"] = state_dict[
+ "backbone_model.embed_tokens.embed_audio_tokens.weight"
+ ].clone()
+ del loaded
+ gc.collect()
+
+ # -------------------------
+ # load the weights and save
+ # -------------------------
+
+ print("Loading the checkpoint in a Csm model.")
+ with torch.device("meta"):
+ model = CsmForConditionalGeneration(config)
+ model.load_state_dict(state_dict, strict=True, assign=True)
+ print("Checkpoint loaded successfully.")
+ del model.config._name_or_path
+
+ # default generation config
+ model.generation_config._from_model_config = False
+ model.generation_config.max_new_tokens = 125
+ model.generation_config.do_sample = True
+ model.generation_config.top_k = 50
+ model.generation_config.temperature = 0.9
+ model.generation_config.depth_decoder_do_sample = True
+ model.generation_config.depth_decoder_top_k = 50
+ model.generation_config.depth_decoder_temperature = 0.9
+
+ print("Saving the model.")
+ model.save_pretrained(output_dir)
+ del state_dict, model
+
+ # Safety check: reload the converted model
+ gc.collect()
+ print("Reloading the model to check if it's saved correctly.")
+ CsmForConditionalGeneration.from_pretrained(output_dir, dtype=torch.bfloat16, device_map="auto")
+ print("Model reloaded successfully.")
+
+
+def write_tokenizer(output_dir):
+ # from https://github.com/SesameAILabs/csm/blob/2d720827843b653c4d67bb4445b1c0a4f59e646f/generator.py#L22-L36
+ def load_llama3_tokenizer():
+ """
+ https://github.com/huggingface/transformers/issues/22794#issuecomment-2092623992
+ """
+ tokenizer_name = "meta-llama/Llama-3.2-1B"
+ tokenizer = AutoTokenizer.from_pretrained(tokenizer_name)
+ bos = tokenizer.bos_token
+ eos = tokenizer.eos_token
+ tokenizer._tokenizer.post_processor = TemplateProcessing(
+ single=f"{bos}:0 $A:0 {eos}:0",
+ pair=f"{bos}:0 $A:0 {eos}:0 {bos}:1 $B:1 {eos}:1",
+ special_tokens=[(f"{bos}", tokenizer.bos_token_id), (f"{eos}", tokenizer.eos_token_id)],
+ )
+
+ return tokenizer
+
+ tokenizer = load_llama3_tokenizer()
+ tokenizer.pad_token = tokenizer.eos_token
+ tokenizer.save_pretrained(output_dir)
+
+ # manually modify in tokenizer_config.json
+ # "128002": {
+ # "content": "<|AUDIO|>",
+ # ...
+ # }
+ # "128003": {
+ # "content": "<|audio_eos|>",
+ # ...
+ # }
+ print(
+ "Tokenizer saved successfully. Please manually modify in tokenizer_config.json AND tokenizer.json as follows: "
+ )
+ print("""
+ # "128002": {
+ # "content": "<|AUDIO|>",
+ # ...
+ # }
+ # "128003": {
+ # "content": "<|audio_eos|>",
+ # ...
+ # }
+ """)
+
+
+def write_processor(output_dir, codec_model_path_or_repo):
+ chat_template = "\n{%- for message in messages %}\n {#-- Validate role is a stringified integer --#}\n {%- if not message['role'] is string or not message['role'].isdigit() %}\n {{- raise_exception(\"The role must be an integer or a stringified integer (e.g. '0') designating the speaker id\") }}\n {%- endif %}\n\n {#-- Validate content is a list --#}\n {%- set content = message['content'] %}\n {%- if content is not iterable or content is string %}\n {{- raise_exception(\"The content must be a list\") }}\n {%- endif %}\n\n {#-- Collect content types --#}\n {%- set content_types = content | map(attribute='type') | list %}\n {%- set is_last = loop.last %}\n\n {#-- Last message validation --#}\n {%- if is_last %}\n {%- if 'text' not in content_types %}\n {{- raise_exception(\"The last message must include one item of type 'text'\") }}\n {%- elif (content_types | select('equalto', 'text') | list | length > 1) or (content_types | select('equalto', 'audio') | list | length > 1) %}\n {{- raise_exception(\"At most two items are allowed in the last message: one 'text' and one 'audio'\") }}\n {%- endif %}\n\n {#-- All other messages validation --#}\n {%- else %}\n {%- if content_types | select('equalto', 'text') | list | length != 1\n or content_types | select('equalto', 'audio') | list | length != 1 %}\n {{- raise_exception(\"Each message (except the last) must contain exactly one 'text' and one 'audio' item\") }}\n {%- elif content_types | reject('in', ['text', 'audio']) | list | length > 0 %}\n {{- raise_exception(\"Only 'text' and 'audio' types are allowed in content\") }}\n {%- endif %}\n {%- endif %}\n{%- endfor %}\n\n{%- for message in messages %}\n {{- bos_token }}\n {{- '[' + message['role'] + ']' }}\n {{- message['content'][0]['text'] }}\n {{- eos_token }}\n {%- if message['content']|length > 1 %}\n {{- '<|AUDIO|><|audio_eos|>' }}\n {%- endif %}\n{%- endfor %}\n"
+ tokenizer = AutoTokenizer.from_pretrained(output_dir)
+ feature_extractor = AutoFeatureExtractor.from_pretrained(codec_model_path_or_repo)
+
+ processor = CsmProcessor(
+ tokenizer=tokenizer,
+ feature_extractor=feature_extractor,
+ chat_template=chat_template,
+ )
+
+ processor.save_pretrained(output_dir)
+ print("Processor saved successfully.")
+
+
+def main():
+ parser = argparse.ArgumentParser(description="Convert Csm weights to HuggingFace format")
+ parser.add_argument(
+ "--input_path_or_repo",
+ type=str,
+ required=True,
+ help="Path or repo containing Csm weights",
+ )
+ parser.add_argument(
+ "--model_name",
+ type=str,
+ required=True,
+ help="Name of the model in input_path_or_repo",
+ )
+ parser.add_argument(
+ "--codec_model_path_or_repo",
+ type=str,
+ required=True,
+ help="Path or repo containing the codec model",
+ )
+ parser.add_argument(
+ "--output_dir",
+ help="Location to write HF model and tokenizer",
+ )
+ args = parser.parse_args()
+
+ write_model(
+ args.input_path_or_repo,
+ args.model_name,
+ args.codec_model_path_or_repo,
+ output_dir=args.output_dir,
+ )
+
+ write_tokenizer(args.output_dir)
+
+ write_processor(args.output_dir, args.codec_model_path_or_repo)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/third_party/transformers/src/transformers/models/csm/generation_csm.py b/third_party/transformers/src/transformers/models/csm/generation_csm.py
new file mode 100644
index 0000000000000000000000000000000000000000..48354709a981686a1996c178d4f80aa01369dde7
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/csm/generation_csm.py
@@ -0,0 +1,487 @@
+# Copyright 2025 The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from dataclasses import dataclass
+from typing import TYPE_CHECKING, Any, Optional
+
+import torch
+import torch.nn as nn
+
+from ...generation import (
+ GenerateDecoderOnlyOutput,
+ GenerationConfig,
+ GenerationMixin,
+ GenerationMode,
+)
+from ...generation.logits_process import LogitsProcessorList
+from ...generation.stopping_criteria import MaxLengthCriteria, StoppingCriteriaList
+from ...generation.utils import GenerateNonBeamOutput
+from ...utils import logging
+
+
+if TYPE_CHECKING:
+ from ...generation.streamers import BaseStreamer
+
+
+logger = logging.get_logger(__name__)
+
+
+@dataclass
+class CsmGenerateOutput(GenerateDecoderOnlyOutput):
+ """
+ Outputs of CsmForConditionalGeneration.generate.
+
+ Args:
+ sequences (`torch.LongTensor` of shape `(batch_size, sequence_length)`):
+ The generated sequences. The second dimension (sequence_length) is either equal to `max_length` or shorter
+ if all batches finished early due to the `eos_token_id`.
+ scores (`tuple(torch.FloatTensor)` *optional*, returned when `output_scores=True`):
+ Processed prediction scores of the language modeling head (scores for each vocabulary token before SoftMax)
+ at each generation step. Tuple of `torch.FloatTensor` with up to `max_new_tokens` elements (one element for
+ each generated token), with each tensor of shape `(batch_size, config.vocab_size)`.
+ logits (`tuple(torch.FloatTensor)` *optional*, returned when `output_logits=True`):
+ Unprocessed prediction scores of the language modeling head (scores for each vocabulary token before SoftMax)
+ at each generation step. Tuple of `torch.FloatTensor` with up to `max_new_tokens` elements (one element for
+ each generated token), with each tensor of shape `(batch_size, config.vocab_size)`.
+ attentions (`tuple(tuple(torch.FloatTensor))`, *optional*, returned when `output_attentions=True`):
+ Tuple (one element for each generated token) of tuples (one element for each layer of the decoder) of
+ `torch.FloatTensor` of shape `(batch_size, num_heads, generated_length, sequence_length)`.
+ hidden_states (`tuple(tuple(torch.FloatTensor))`, *optional*, returned when `output_hidden_states=True`):
+ Tuple (one element for each generated token) of tuples (one element for each layer of the decoder) of
+ `torch.FloatTensor` of shape `(batch_size, generated_length, hidden_size)`.
+ past_key_values (`Cache`, *optional*, returned when `use_cache=True`):
+ Returns the model cache, used to speed up decoding. Different models have a different cache format, check
+ audio (`list(torch.FloatTensor)` of length `batch_size`):
+ The generated audio.
+ """
+
+ audio: list[torch.Tensor] | None = None
+
+
+class CsmGenerationMixin(GenerationMixin):
+ def _get_stopping_criteria(
+ self,
+ *args,
+ **kwargs,
+ ) -> StoppingCriteriaList:
+ criteria = super()._get_stopping_criteria(*args, **kwargs)
+
+ kept_criteria = StoppingCriteriaList()
+ for criterion in criteria:
+ if not isinstance(criterion, MaxLengthCriteria):
+ logger.warning(
+ f"Csm does not support {criterion.__class__.__name__} stopping criteria, it will be ignored."
+ )
+ else:
+ kept_criteria.append(criterion)
+ return kept_criteria
+
+ def _prepare_generation_config(
+ self, generation_config: GenerationConfig | None, **kwargs: Any
+ ) -> tuple[GenerationConfig, dict]:
+ """
+ This method overrides [~generation.utils.GenerationMixin._prepare_generation_config].
+ It ensures that the depth decoder generation config is initialized and that passed args as depth_decoder_* are properly handled.
+ """
+ # extract depth decoder kwargs and remove them from the main kwargs
+ depth_decoder_kwargs = {
+ k[len("depth_decoder_") :]: v for k, v in kwargs.items() if k.startswith("depth_decoder_")
+ }
+
+ # remove the depth decoder keys from the original kwargs
+ kwargs = {k: v for k, v in kwargs.items() if not k.startswith("depth_decoder_")}
+
+ # initialize the generation config
+ generation_config, model_kwargs = super()._prepare_generation_config(generation_config, **kwargs)
+ self.depth_decoder.generation_config.update(**depth_decoder_kwargs)
+
+ # ensure the depth decoder generation config is valid
+ depth_decoder_min_new_tokens = getattr(self.depth_decoder.generation_config, "min_new_tokens") or (
+ self.config.num_codebooks - 1
+ )
+ depth_decoder_max_new_tokens = getattr(self.depth_decoder.generation_config, "max_new_tokens") or (
+ self.config.num_codebooks - 1
+ )
+
+ if {depth_decoder_min_new_tokens, depth_decoder_max_new_tokens} != {self.config.num_codebooks - 1}:
+ raise ValueError(
+ f"depth_decoder_generation_config's min_new_tokens ({depth_decoder_min_new_tokens}) and max_new_tokens ({depth_decoder_max_new_tokens}) must be equal to self.config.num_codebooks - 1 ({self.config.num_codebooks - 1})"
+ )
+ elif self.depth_decoder.generation_config.return_dict_in_generate:
+ logger.warning(
+ "depth_decoder_generation_config.return_dict_in_generate is set to True, but this will be ignored as the depth decoder model does not return a dictionary in generate"
+ )
+ self.depth_decoder.generation_config.return_dict_in_generate = False
+
+ self.depth_decoder.generation_config.min_new_tokens = depth_decoder_min_new_tokens
+ self.depth_decoder.generation_config.max_new_tokens = depth_decoder_max_new_tokens
+
+ # Monkey patch the get_generation_mode method to support CSM model
+ original_get_generation_mode = generation_config.get_generation_mode
+
+ def patched_get_generation_mode(assistant_model=None):
+ generation_mode = original_get_generation_mode(assistant_model)
+ if generation_mode not in [GenerationMode.GREEDY_SEARCH, GenerationMode.SAMPLE]:
+ raise ValueError(
+ f"Generation mode {generation_mode} is not supported for CSM model. Please set generation parameters to use greedy or sampling generation."
+ )
+
+ return generation_mode
+
+ generation_config.get_generation_mode = patched_get_generation_mode
+
+ return generation_config, model_kwargs
+
+ def _sample(
+ self,
+ input_ids: torch.LongTensor,
+ logits_processor: LogitsProcessorList,
+ stopping_criteria: StoppingCriteriaList,
+ generation_config: GenerationConfig,
+ synced_gpus: bool = False,
+ streamer: Optional["BaseStreamer"] = None,
+ **model_kwargs,
+ ) -> GenerateNonBeamOutput | torch.LongTensor:
+ """
+ This method overrides [~generation.utils.GenerationMixin._sample].
+ To ease maintenance, modifications are marked with the comment "Csm specific".
+
+ Indeed, Csm model requires a custom generation sampling step:
+ 1. Infer the backbone model to sample the first codebook token
+ 2. Call generate on the depth decoder with the first codebook token as input_ids to sample the next codebook tokens
+ 3. Use these generated codebook tokens as input_ids to sample the next first codebook token using the backbone model
+ 4. Repeat until stopping criteria is met
+
+ Csm supports two stopping criteria:
+ - stop when the generated sequence is at max_length
+ - stop when all the generated codebook tokens are the codebook_eos_token_id
+ """
+ # init values
+ # *************** Csm specific ***************
+ pad_token_id = self.config.codebook_pad_token_id
+ has_eos_stopping_criteria = generation_config._eos_token_tensor is not None
+ # ============================================
+ output_attentions = generation_config.output_attentions
+ output_hidden_states = generation_config.output_hidden_states
+ output_scores = generation_config.output_scores
+ output_logits = generation_config.output_logits
+ return_dict_in_generate = generation_config.return_dict_in_generate
+ do_sample = generation_config.do_sample
+
+ # init attention / hidden states / scores tuples
+ scores = () if (return_dict_in_generate and output_scores) else None
+ raw_logits = () if (return_dict_in_generate and output_logits) else None
+ decoder_attentions = () if (return_dict_in_generate and output_attentions) else None
+ decoder_hidden_states = () if (return_dict_in_generate and output_hidden_states) else None
+
+ # keep track of which sequences are already finished
+ batch_size, cur_len = input_ids.shape[:2]
+ this_peer_finished = False
+ unfinished_sequences = torch.ones(batch_size, dtype=torch.long, device=input_ids.device)
+
+ # *************** Csm specific ***************
+ if input_ids.ndim == 2 and model_kwargs.get("inputs_embeds") is None:
+ # in the case where the passed input_ids correspond to text tokens, i.e. don't have a third dimension for codebook ids,
+ # we need to remove the input length to the MaxLengthCriteria stopping criteria has such input are not returned
+ for criterion in stopping_criteria:
+ if isinstance(criterion, MaxLengthCriteria):
+ criterion.max_length -= cur_len
+ # ============================================
+
+ model_forward = (
+ self.get_compiled_call(generation_config.compile_config)
+ if self._valid_auto_compile_criteria(model_kwargs, generation_config)
+ else self.__call__
+ )
+
+ # *************** Csm specific ***************
+ model_kwargs.update({"output_hidden_states": True})
+
+ prefill_consumed = False
+ outputs = self._prefill(
+ input_ids,
+ generation_config,
+ model_kwargs,
+ is_first_iteration=not generation_config.is_assistant,
+ )
+
+ while self._has_unfinished_sequences(this_peer_finished, synced_gpus, device=input_ids.device):
+ if prefill_consumed:
+ next_sequence_length = 1 if model_kwargs["use_cache"] else None
+ model_inputs = self.prepare_inputs_for_generation(
+ input_ids, next_sequence_length=next_sequence_length, **model_kwargs
+ )
+ # prepare variable output controls (note: some models won't accept all output controls)
+ model_inputs.update({"output_attentions": output_attentions} if output_attentions else {})
+ outputs = model_forward(**model_inputs, return_dict=True)
+ prefill_consumed = True
+
+ # synced_gpus: don't waste resources running the code we don't need; kwargs must be updated before skipping
+ model_kwargs = self._update_model_kwargs_for_generation(
+ outputs,
+ model_kwargs,
+ )
+ if synced_gpus and this_peer_finished:
+ continue
+
+ # Clone is needed to avoid keeping a hanging ref to outputs.logits which may be very large for first iteration
+ # (the clone itself is always small)
+ next_token_logits = outputs.logits[:, -1, :].clone().float()
+ next_token_logits = next_token_logits.to(input_ids.device)
+
+ # pre-process distribution
+ next_token_scores = logits_processor(input_ids, next_token_logits)
+
+ # Store scores, attentions and hidden_states when required
+ if return_dict_in_generate:
+ if output_scores:
+ scores += (next_token_scores,)
+ if output_logits:
+ raw_logits += (next_token_logits,)
+ if output_attentions:
+ decoder_attentions += (outputs.attentions,)
+
+ if output_hidden_states:
+ decoder_hidden_states += (outputs.hidden_states,)
+
+ # token selection
+ if do_sample:
+ probs = nn.functional.softmax(next_token_scores, dim=-1)
+ # TODO (joao): this OP throws "skipping cudagraphs due to ['incompatible ops']", find solution
+ next_tokens = torch.multinomial(probs, num_samples=1).squeeze(1)
+ else:
+ next_tokens = torch.argmax(next_token_scores, dim=-1)
+
+ # *************** Csm specific ***************
+ # infer the depth decoder
+ first_codebook_ids = next_tokens[:, None]
+ # adds place holder in position 0 that will be replaced by the backbone_last_hidden_state
+ depth_decoder_input_ids = nn.functional.pad(first_codebook_ids, (1, 0), value=0)
+ backbone_last_hidden_state = outputs.hidden_states[-1][:, -1, :]
+
+ depth_decoder_outputs = self.depth_decoder.generate(
+ input_ids=depth_decoder_input_ids, backbone_last_hidden_state=backbone_last_hidden_state.clone()
+ )
+ codebook_ids = (
+ depth_decoder_outputs
+ if isinstance(depth_decoder_outputs, torch.Tensor)
+ else depth_decoder_outputs.sequences
+ )
+ # remove the place holder in position 0
+ codebook_ids = codebook_ids[:, 1:]
+ next_tokens = codebook_ids
+
+ # finished sentences should have their next token be a padding token
+ if has_eos_stopping_criteria:
+ next_tokens = next_tokens * unfinished_sequences.unsqueeze(-1) + pad_token_id * (
+ 1 - unfinished_sequences.unsqueeze(-1)
+ )
+
+ # update generated ids, model inputs, and length for next step
+ if input_ids.ndim == 2:
+ input_ids = next_tokens[:, None, :]
+ else:
+ input_ids = torch.cat([input_ids, next_tokens[:, None, :]], dim=1)
+ # ============================================
+
+ if streamer is not None:
+ streamer.put(next_tokens.cpu())
+
+ # *************** Csm specific ***************
+ # for the eos stopping criteria, is it expected that the eos token is the same for each codebook !!!!
+ unfinished_sequences = unfinished_sequences & ~(
+ input_ids[:, -1, :-1] == self.config.codebook_eos_token_id
+ ).all(-1)
+ # ============================================
+ unfinished_sequences = unfinished_sequences & ~stopping_criteria(input_ids, scores)
+ this_peer_finished = unfinished_sequences.max() == 0
+ cur_len += 1
+
+ # This is needed to properly delete outputs.logits which may be very large for first iteration
+ # Otherwise a reference to outputs is kept which keeps the logits alive in the next iteration
+ del outputs
+
+ # *************** Csm specific ***************
+ del depth_decoder_outputs
+ # ============================================
+
+ if streamer is not None:
+ streamer.end()
+
+ if return_dict_in_generate:
+ return GenerateDecoderOnlyOutput(
+ sequences=input_ids,
+ scores=scores,
+ logits=raw_logits,
+ attentions=decoder_attentions,
+ hidden_states=decoder_hidden_states,
+ past_key_values=model_kwargs.get("past_key_values"),
+ )
+ else:
+ return input_ids
+
+ def generate(
+ self,
+ input_ids: torch.Tensor | None = None,
+ input_values: torch.Tensor | None = None,
+ input_values_cutoffs: torch.Tensor | None = None,
+ generation_config: GenerationConfig | None = None,
+ logits_processor: LogitsProcessorList | None = None,
+ stopping_criteria: StoppingCriteriaList | None = None,
+ synced_gpus: bool | None = None,
+ streamer: Optional["BaseStreamer"] = None,
+ output_audio: bool | None = False,
+ **kwargs,
+ ) -> GenerateNonBeamOutput | torch.LongTensor:
+ r"""
+ This method overrides [`~generation.utils.GenerationMixin.generate`] to match the specifics of the Csm model.
+ Indeed, Csm model requires a custom generation sampling step:
+ 1. Infer the backbone model to sample the first codebook token
+ 2. Call generate on the depth decoder with the first codebook token as `input_ids` to sample the next codebook tokens
+ 3. Use these generated codebook tokens as `input_ids` to sample the next first codebook token using the backbone model
+ 4. Repeat until stopping criteria is met
+
+
+
+ Most generation-controlling parameters are set in `generation_config` which, if not passed, will be set to the
+ model's default generation configuration. You can override any `generation_config` by passing the corresponding
+ parameters to generate(), e.g. `.generate(inputs, do_sample=True)`.
+
+
+ Parameters:
+ inputs_ids (`torch.Tensor` of shape (batch_size, seq_length), *optional*):
+ The sequence used as a prompt for the backbone model.
+ input_values (`torch.Tensor` of shape (batch_size, channels, max_concatenated_audio_length), *optional*):
+ The batched audio input values, where each batch entry contains the concatenation of all audio segments for that entry.
+ These values will be encoded into codebook tokens using the codec model and merged with the text input ids provided in `input_ids`.
+ input_values_cutoffs (`torch.Tensor` of shape (batch_size, max_num_audio), *optional*):
+ Specify the end positions of audio segments within each batch entry, relative to the concatenated audio input.
+ If a batch entry has fewer segments than the maximum, it is padded with -1. For example, in a batch of 2 sequences
+ where the first contains 2 audio segments of length l1, and the second contains 1 audio segment of length l2,
+ the input_values_cutoffs would be: [[l1, 2 * l1], [l2, -1]].
+ generation_config ([`~generation.GenerationConfig`], *optional*):
+ The generation configuration to be used as base parametrization for the generation call. `**kwargs`
+ passed to generate matching the attributes of `generation_config` will override them. If
+ `generation_config` is not provided, the default will be used, which has the following loading
+ priority: 1) from the `generation_config.json` model file, if it exists; 2) from the model
+ configuration. Please note that unspecified parameters will inherit [`~generation.GenerationConfig`]'s
+ default values, whose documentation should be checked to parameterize generation.
+ logits_processor (`LogitsProcessorList`, *optional*):
+ Custom logits processors that complement the default logits processors built from arguments and
+ generation config. If a logit processor is passed that is already created with the arguments or a
+ generation config an error is thrown. This feature is intended for advanced users.
+ stopping_criteria (`StoppingCriteriaList`, *optional*):
+ Custom stopping criteria that complements the default stopping criteria built from arguments and a
+ generation config. If a stopping criteria is passed that is already created with the arguments or a
+ generation config an error is thrown. If your stopping criteria depends on the `scores` input, make
+ sure you pass `return_dict_in_generate=True, output_scores=True` to `generate`. This feature is
+ intended for advanced users.
+ synced_gpus (`bool`, *optional*):
+ Whether to continue running the while loop until max_length. Unless overridden, this flag will be set
+ to `True` if using `FullyShardedDataParallel` or DeepSpeed ZeRO Stage 3 with multiple GPUs to avoid
+ deadlocking if one GPU finishes generating before other GPUs. Otherwise, defaults to `False`.
+ streamer (`BaseStreamer`, *optional*):
+ Streamer object that will be used to stream the generated sequences. Generated tokens are passed
+ through `streamer.put(token_ids)` and the streamer is responsible for any further processing.
+ output_audio (`bool`, *optional*):
+ Whether to return the generated audio.
+ kwargs (`dict[str, Any]`, *optional*):
+ Ad hoc parametrization of `generation_config` and/or additional model-specific kwargs that will be
+ forwarded to the `forward` function of the model. Depth decoder specific kwargs should be prefixed with *depth_decoder_*.
+
+ Return:
+ [`CsmGenerateOutput`] or `torch.LongTensor` or `list[torch.FloatTensor]`: A [`CsmGenerateOutput`]
+ (if `return_dict_in_generate=True` or when `config.return_dict_in_generate=True`) or a `torch.LongTensor` when `output_audio=False`
+ or a `list[torch.FloatTensor]` otherwise.
+
+ Example:
+
+ ```python
+ >>> from transformers import CsmProcessor, CsmForConditionalGeneration
+ >>> from datasets import load_dataset, Audio
+
+ >>> model_id = "sesame/csm-1b"
+ >>> torch_device = "cuda" if torch.cuda.is_available() else "cpu"
+
+ >>> processor = AutoProcessor.from_pretrained(model_id)
+
+ >>> ds = load_dataset("hf-internal-testing/dailytalk-dummy", split="train")
+ >>> # ensure the audio is 24kHz
+ >>> ds = ds.cast_column("audio", Audio(sampling_rate=24000))
+
+ >>> conversation = []
+ >>> # prepare a conversation with text and corresponding audio
+ >>> for text, audio, speaker_id in zip(ds[:4]["text"], ds[:4]["audio"], ds[:4]["speaker_id"]):
+ ... conversation.append(
+ ... {
+ ... "role": f"{speaker_id}",
+ ... "content": [{"type": "text", "text": text}, {"type": "audio", "path": audio["array"]}],
+ ... }
+ ... )
+
+ >>> # text prompt
+ >>> conversation.append({"role": f"{ds[4]['speaker_id']}", "content": [{"type": "text", "text": ds[4]["text"]}]})
+
+ >>> inputs = processor.apply_chat_template(
+ ... conversation,
+ ... tokenize=True,
+ ... return_dict=True,
+ ... ).to(torch_device)
+
+ >>> model = CsmForConditionalGeneration.from_pretrained(model_id, device_map=torch_device)
+ >>> audio = model.generate(**inputs, output_audio=True)
+ >>> processor.save_audio(audio, "output.wav")
+ ```
+ """
+ generate_output = super().generate(
+ input_ids=input_ids,
+ input_values=input_values,
+ input_values_cutoffs=input_values_cutoffs,
+ generation_config=generation_config,
+ logits_processor=logits_processor,
+ stopping_criteria=stopping_criteria,
+ synced_gpus=synced_gpus,
+ streamer=streamer,
+ **kwargs,
+ )
+
+ generate_returned_dict = not isinstance(generate_output, torch.Tensor)
+ audio = None
+ if output_audio:
+ generated_audio_codes = generate_output.sequences if generate_returned_dict else generate_output
+
+ # infer the codec model
+ audio = []
+ with torch.no_grad():
+ # =======================================
+ # TODO: @eustlb, this should be batched !!!
+ # but requires making sure batched inference of the codec model works as intended
+ for audio_codes_batch in generated_audio_codes:
+ eos_idxs = (audio_codes_batch == self.config.codebook_eos_token_id).all(dim=-1).nonzero()
+ if eos_idxs.numel() != 0:
+ cutoff_idx = eos_idxs.min()
+ else:
+ cutoff_idx = audio_codes_batch.shape[0]
+
+ audio_codes_batch = audio_codes_batch[:cutoff_idx]
+ codec_decode_output = self.codec_model.decode(audio_codes_batch.transpose(0, 1).unsqueeze(0))
+ audio.append(codec_decode_output.audio_values[0, 0])
+ # =======================================
+
+ if generate_returned_dict:
+ return CsmGenerateOutput(audio=audio, **generate_output)
+ elif output_audio:
+ return audio
+ else:
+ return generate_output
diff --git a/third_party/transformers/src/transformers/models/csm/modeling_csm.py b/third_party/transformers/src/transformers/models/csm/modeling_csm.py
new file mode 100644
index 0000000000000000000000000000000000000000..eb78dca8faf5e6c15b6e0fda419677e25af7f34a
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/csm/modeling_csm.py
@@ -0,0 +1,1094 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/csm/modular_csm.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_csm.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2025 Sesame and The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from collections.abc import Callable
+from dataclasses import dataclass
+from typing import Optional
+
+import torch
+import torch.nn as nn
+
+from ... import initialization as init
+from ...activations import ACT2FN
+from ...cache_utils import Cache, DynamicCache
+from ...generation import GenerationMixin
+from ...integrations import use_kernel_forward_from_hub, use_kernel_func_from_hub, use_kernelized_func
+from ...masking_utils import create_causal_mask
+from ...modeling_layers import GradientCheckpointingLayer
+from ...modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast
+from ...modeling_rope_utils import ROPE_INIT_FUNCTIONS, dynamic_rope_update
+from ...modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
+from ...processing_utils import Unpack
+from ...utils import ModelOutput, TransformersKwargs, auto_docstring, can_return_tuple, logging
+from ...utils.generic import maybe_autocast, merge_with_config_defaults
+from ...utils.import_utils import is_torchdynamo_compiling
+from ...utils.output_capturing import capture_outputs
+from ..auto import AutoModel
+from .configuration_csm import CsmConfig, CsmDepthDecoderConfig
+from .generation_csm import CsmGenerationMixin
+
+
+logger = logging.get_logger(__name__)
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ Base class for the model autoregressive outputs.
+ """
+)
+class CsmOutputWithPast(ModelOutput):
+ r"""
+ loss (`torch.FloatTensor` of shape `(1,)`, *optional*, returned when `labels` is provided):
+ Language modeling loss (for next-token prediction).
+ logits (`torch.FloatTensor` of shape `(batch_size, sequence_length, config.vocab_size)`):
+ Prediction scores of the language modeling head (scores for each vocabulary token before SoftMax).
+ past_key_values (`Cache`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
+ It is a [`~cache_utils.Cache`] instance. For more details, see our [kv cache guide](https://huggingface.co/docs/transformers/en/kv_cache).
+
+ Contains pre-computed hidden-states (key and values in the self-attention blocks) that can be used (see
+ `past_key_values` input) to speed up sequential decoding.
+ depth_decoder_loss (`torch.FloatTensor` of shape `(1,)`, *optional*, returned when `labels` is provided):
+ Language modeling loss (for next-token prediction) of the depth decoder model.
+ depth_decoder_logits (`torch.FloatTensor` of shape `(batch_size, sequence_length, config.vocab_size)`):
+ Prediction scores of the depth decoder (scores for each vocabulary token before SoftMax).
+ depth_decoder_past_key_values (`Cache`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
+ It is a [`~cache_utils.Cache`] instance. For more details, see our [kv cache guide](https://huggingface.co/docs/transformers/en/kv_cache).
+ depth_decoder_hidden_states (`tuple(torch.FloatTensor)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
+ Tuple of `torch.FloatTensor` (one for the output of the embeddings, if the model has an embedding layer, +
+ one for the output of each layer) of shape `(batch_size, sequence_length, hidden_size)`.
+
+ Hidden-states of the model at the output of each layer plus the optional initial embedding outputs.
+ depth_decoder_attentions (`tuple(torch.FloatTensor)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`):
+ Tuple of `torch.FloatTensor` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
+ sequence_length)`.
+ backbone_loss (`torch.FloatTensor` of shape `(1,)`, *optional*, returned when `labels` is provided):
+ Language modeling loss (for next-token prediction) of the backbone model.
+ """
+
+ loss: torch.FloatTensor | None = None
+ logits: torch.FloatTensor | None = None
+ past_key_values: Cache | None = None
+ hidden_states: tuple[torch.FloatTensor, ...] | None = None
+ attentions: tuple[torch.FloatTensor, ...] | None = None
+ depth_decoder_loss: torch.FloatTensor | None = None
+ depth_decoder_logits: torch.FloatTensor | None = None
+ depth_decoder_past_key_values: Cache | None = None
+ depth_decoder_hidden_states: tuple[torch.FloatTensor, ...] | None = None
+ depth_decoder_attentions: tuple[torch.FloatTensor, ...] | None = None
+ backbone_loss: torch.FloatTensor | None = None
+
+
+@use_kernel_forward_from_hub("RMSNorm")
+class CsmRMSNorm(nn.Module):
+ def __init__(self, hidden_size, eps: float = 1e-6) -> None:
+ """
+ CsmRMSNorm is equivalent to T5LayerNorm
+ """
+ super().__init__()
+ self.weight = nn.Parameter(torch.ones(hidden_size))
+ self.variance_epsilon = eps
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ input_dtype = hidden_states.dtype
+ hidden_states = hidden_states.to(torch.float32)
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
+ hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
+ return self.weight * hidden_states.to(input_dtype)
+
+ def extra_repr(self):
+ return f"{tuple(self.weight.shape)}, eps={self.variance_epsilon}"
+
+
+class CsmRotaryEmbedding(nn.Module):
+ inv_freq: torch.Tensor # fix linting for `register_buffer`
+
+ def __init__(self, config: CsmConfig, device=None):
+ super().__init__()
+ self.max_seq_len_cached = config.max_position_embeddings
+ self.original_max_seq_len = config.max_position_embeddings
+
+ self.config = config
+
+ self.rope_type = self.config.rope_parameters["rope_type"]
+ rope_init_fn: Callable = self.compute_default_rope_parameters
+ if self.rope_type != "default":
+ rope_init_fn = ROPE_INIT_FUNCTIONS[self.rope_type]
+ inv_freq, self.attention_scaling = rope_init_fn(self.config, device)
+
+ self.register_buffer("inv_freq", inv_freq, persistent=False)
+ self.register_buffer("original_inv_freq", inv_freq.clone(), persistent=False)
+
+ @staticmethod
+ def compute_default_rope_parameters(
+ config: CsmConfig | None = None,
+ device: Optional["torch.device"] = None,
+ seq_len: int | None = None,
+ ) -> tuple["torch.Tensor", float]:
+ """
+ Computes the inverse frequencies according to the original RoPE implementation
+ Args:
+ config ([`~transformers.PreTrainedConfig`]):
+ The model configuration.
+ device (`torch.device`):
+ The device to use for initialization of the inverse frequencies.
+ seq_len (`int`, *optional*):
+ The current sequence length. Unused for this type of RoPE.
+ Returns:
+ Tuple of (`torch.Tensor`, `float`), containing the inverse frequencies for the RoPE embeddings and the
+ post-processing scaling factor applied to the computed cos/sin (unused in this type of RoPE).
+ """
+ base = config.rope_parameters["rope_theta"]
+ dim = getattr(config, "head_dim", None) or config.hidden_size // config.num_attention_heads
+
+ attention_factor = 1.0 # Unused in this type of RoPE
+
+ # Compute the inverse frequencies
+ inv_freq = 1.0 / (
+ base ** (torch.arange(0, dim, 2, dtype=torch.int64).to(device=device, dtype=torch.float) / dim)
+ )
+ return inv_freq, attention_factor
+
+ @torch.no_grad()
+ @dynamic_rope_update # power user: used with advanced RoPE types (e.g. dynamic rope)
+ def forward(self, x, position_ids):
+ inv_freq_expanded = self.inv_freq[None, :, None].float().expand(position_ids.shape[0], -1, 1).to(x.device)
+ position_ids_expanded = position_ids[:, None, :].float()
+
+ device_type = x.device.type if isinstance(x.device.type, str) and x.device.type != "mps" else "cpu"
+ with maybe_autocast(device_type=device_type, enabled=False): # Force float32
+ freqs = (inv_freq_expanded.float() @ position_ids_expanded.float()).transpose(1, 2)
+ emb = torch.cat((freqs, freqs), dim=-1)
+ cos = emb.cos() * self.attention_scaling
+ sin = emb.sin() * self.attention_scaling
+
+ return cos.to(dtype=x.dtype), sin.to(dtype=x.dtype)
+
+
+class CsmMLP(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+ self.config = config
+ self.hidden_size = config.hidden_size
+ self.intermediate_size = config.intermediate_size
+ self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=config.mlp_bias)
+ self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=config.mlp_bias)
+ self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=config.mlp_bias)
+ self.act_fn = ACT2FN[config.hidden_act]
+
+ def forward(self, x):
+ down_proj = self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
+ return down_proj
+
+
+def rotate_half(x):
+ """Rotates half the hidden dims of the input."""
+ x1 = x[..., : x.shape[-1] // 2]
+ x2 = x[..., x.shape[-1] // 2 :]
+ return torch.cat((-x2, x1), dim=-1)
+
+
+@use_kernel_func_from_hub("rotary_pos_emb")
+def apply_rotary_pos_emb(q, k, cos, sin, unsqueeze_dim=1):
+ """Applies Rotary Position Embedding to the query and key tensors.
+
+ Args:
+ q (`torch.Tensor`): The query tensor.
+ k (`torch.Tensor`): The key tensor.
+ cos (`torch.Tensor`): The cosine part of the rotary embedding.
+ sin (`torch.Tensor`): The sine part of the rotary embedding.
+ unsqueeze_dim (`int`, *optional*, defaults to 1):
+ The 'unsqueeze_dim' argument specifies the dimension along which to unsqueeze cos[position_ids] and
+ sin[position_ids] so that they can be properly broadcasted to the dimensions of q and k. For example, note
+ that cos[position_ids] and sin[position_ids] have the shape [batch_size, seq_len, head_dim]. Then, if q and
+ k have the shape [batch_size, heads, seq_len, head_dim], then setting unsqueeze_dim=1 makes
+ cos[position_ids] and sin[position_ids] broadcastable to the shapes of q and k. Similarly, if q and k have
+ the shape [batch_size, seq_len, heads, head_dim], then set unsqueeze_dim=2.
+ Returns:
+ `tuple(torch.Tensor)` comprising of the query and key tensors rotated using the Rotary Position Embedding.
+ """
+ cos = cos.unsqueeze(unsqueeze_dim)
+ sin = sin.unsqueeze(unsqueeze_dim)
+ q_embed = (q * cos) + (rotate_half(q) * sin)
+ k_embed = (k * cos) + (rotate_half(k) * sin)
+ return q_embed, k_embed
+
+
+def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
+ """
+ This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
+ num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
+ """
+ batch, num_key_value_heads, slen, head_dim = hidden_states.shape
+ if n_rep == 1:
+ return hidden_states
+ hidden_states = hidden_states[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim)
+ return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
+
+
+def eager_attention_forward(
+ module: nn.Module,
+ query: torch.Tensor,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ attention_mask: torch.Tensor | None,
+ scaling: float,
+ dropout: float = 0.0,
+ **kwargs: Unpack[TransformersKwargs],
+):
+ key_states = repeat_kv(key, module.num_key_value_groups)
+ value_states = repeat_kv(value, module.num_key_value_groups)
+
+ attn_weights = torch.matmul(query, key_states.transpose(2, 3)) * scaling
+ if attention_mask is not None:
+ attn_weights = attn_weights + attention_mask
+
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query.dtype)
+ attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
+ attn_output = torch.matmul(attn_weights, value_states)
+ attn_output = attn_output.transpose(1, 2).contiguous()
+
+ return attn_output, attn_weights
+
+
+@use_kernelized_func(apply_rotary_pos_emb)
+class CsmAttention(nn.Module):
+ """Multi-headed attention from 'Attention Is All You Need' paper"""
+
+ def __init__(self, config: CsmConfig, layer_idx: int):
+ super().__init__()
+ self.config = config
+ self.layer_idx = layer_idx
+ self.head_dim = getattr(config, "head_dim", config.hidden_size // config.num_attention_heads)
+ self.num_key_value_groups = config.num_attention_heads // config.num_key_value_heads
+ self.scaling = self.head_dim**-0.5
+ self.attention_dropout = config.attention_dropout
+ self.is_causal = True
+
+ self.q_proj = nn.Linear(
+ config.hidden_size, config.num_attention_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.k_proj = nn.Linear(
+ config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.v_proj = nn.Linear(
+ config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.o_proj = nn.Linear(
+ config.num_attention_heads * self.head_dim, config.hidden_size, bias=config.attention_bias
+ )
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
+ attention_mask: torch.Tensor | None = None,
+ past_key_values: Cache | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple[torch.Tensor, torch.Tensor]:
+ input_shape = hidden_states.shape[:-1]
+ hidden_shape = (*input_shape, -1, self.head_dim)
+
+ query_states = self.q_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+ key_states = self.k_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+ value_states = self.v_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+
+ cos, sin = position_embeddings
+ query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin)
+
+ if past_key_values is not None:
+ key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx)
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ query_states,
+ key_states,
+ value_states,
+ attention_mask,
+ dropout=0.0 if not self.training else self.attention_dropout,
+ scaling=self.scaling,
+ **kwargs,
+ )
+
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
+ attn_output = self.o_proj(attn_output)
+ return attn_output, attn_weights
+
+
+class CsmDecoderLayer(GradientCheckpointingLayer):
+ def __init__(self, config: CsmConfig, layer_idx: int):
+ super().__init__()
+ self.hidden_size = config.hidden_size
+
+ self.self_attn = CsmAttention(config=config, layer_idx=layer_idx)
+
+ self.mlp = CsmMLP(config)
+ self.input_layernorm = CsmRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.post_attention_layernorm = CsmRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ use_cache: bool | None = False,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> torch.Tensor:
+ residual = hidden_states
+ hidden_states = self.input_layernorm(hidden_states)
+ # Self Attention
+ hidden_states, _ = self.self_attn(
+ hidden_states=hidden_states,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ position_embeddings=position_embeddings,
+ **kwargs,
+ )
+ hidden_states = residual + hidden_states
+
+ # Fully Connected
+ residual = hidden_states
+ hidden_states = self.post_attention_layernorm(hidden_states)
+ hidden_states = self.mlp(hidden_states)
+ hidden_states = residual + hidden_states
+ return hidden_states
+
+
+@auto_docstring(
+ custom_intro="""
+ The bare Csm Model outputting raw hidden-states without any specific head on top.
+ """
+)
+@auto_docstring
+class CsmPreTrainedModel(PreTrainedModel):
+ config: CsmConfig
+ base_model_prefix = "model"
+ input_modalities = ("audio", "text")
+ supports_gradient_checkpointing = True
+ _no_split_modules = ["CsmDecoderLayer"]
+ _skip_keys_device_placement = ["past_key_values"]
+ _supports_flash_attn = True
+ _supports_sdpa = True
+ # does not because of Mimi codec model
+ # _supports_flex_attn = True
+
+ _can_compile_fullgraph = True
+ _supports_attention_backend = True
+ _can_record_outputs = {
+ "hidden_states": CsmDecoderLayer,
+ "attentions": CsmAttention,
+ }
+
+ @torch.no_grad()
+ def _init_weights(self, module):
+ super()._init_weights(module)
+ if isinstance(module, CsmCodebooksHead):
+ num_codebooks = module.num_codebooks
+ for i in range(num_codebooks - 1):
+ init.normal_(module.weight, mean=0.0, std=self.config.initializer_range)
+ elif isinstance(module, CsmBackboneModelEmbeddings):
+ init.copy_(module.audio_tokens_offsets, torch.arange(self.config.num_codebooks) * self.config.vocab_size)
+
+
+@auto_docstring
+class CsmDepthDecoderModel(CsmPreTrainedModel):
+ config: CsmDepthDecoderConfig
+
+ def __init__(self, config):
+ super().__init__(config)
+ self.padding_idx = config.pad_token_id
+ self.vocab_size = config.vocab_size
+ self.embed_tokens = nn.Embedding((config.num_codebooks * config.vocab_size), config.backbone_hidden_size)
+ self.layers = nn.ModuleList(
+ [CsmDecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
+ )
+ self.norm = CsmRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.rotary_emb = CsmRotaryEmbedding(config=config)
+ self.gradient_checkpointing = False
+ self.inputs_embeds_projector = nn.Linear(config.backbone_hidden_size, config.hidden_size, bias=False)
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @merge_with_config_defaults
+ @capture_outputs
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ backbone_last_hidden_state: torch.FloatTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ use_cache: bool | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | BaseModelOutputWithPast:
+ r"""
+ backbone_last_hidden_state (`torch.FloatTensor` of shape `(batch_size, backbone_hidden_size)`, *optional*):
+ The last hidden state of the backbone model. Such input is required when the first codebook token (the one generated by the backbone model)
+ is provided in the `input_ids` argument.
+ """
+ if position_ids is not None and not is_torchdynamo_compiling():
+ logger.warning_once(
+ "Custom `position_ids` were provided but will be ignored. CSM depth decoder automatically determines position_ids "
+ "and as it requires them to be identical across the batch, the provided position_ids will be ignored."
+ )
+ position_ids = None
+ if (input_ids is None) ^ (inputs_embeds is not None):
+ raise ValueError("You must specify exactly one of input_ids or inputs_embeds.")
+
+ if use_cache and past_key_values is None:
+ past_key_values = DynamicCache(config=self.config)
+
+ past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
+ inputs_seq_length = inputs_embeds.shape[1] if inputs_embeds is not None else input_ids.shape[1]
+ device = inputs_embeds.device if inputs_embeds is not None else input_ids.device
+ position_ids = torch.arange(past_seen_tokens, past_seen_tokens + inputs_seq_length, device=device)
+
+ if inputs_embeds is None:
+ codebook_idxs = torch.clamp(position_ids - 1, min=0)
+ offset = codebook_idxs * self.vocab_size
+ inputs_embeds = self.embed_tokens(input_ids + offset)
+
+ input_ids_are_first_codebook = position_ids[0] == 0
+ if backbone_last_hidden_state is not None:
+ inputs_embeds[:, 0] = backbone_last_hidden_state
+ else:
+ if not is_torchdynamo_compiling() and input_ids_are_first_codebook:
+ logger.warning(
+ "When the first codebook token is provided, `backbone_last_hidden_state` should also be provided for correct inference."
+ )
+
+ inputs_embeds = self.inputs_embeds_projector(inputs_embeds)
+
+ causal_mask = create_causal_mask(
+ config=self.config,
+ inputs_embeds=inputs_embeds,
+ attention_mask=attention_mask,
+ past_key_values=past_key_values,
+ position_ids=position_ids,
+ )
+
+ hidden_states = inputs_embeds
+
+ # create position embeddings to be shared across the decoder layers
+ position_ids = position_ids.unsqueeze(0)
+ position_embeddings = self.rotary_emb(hidden_states, position_ids=position_ids)
+
+ for decoder_layer in self.layers[: self.config.num_hidden_layers]:
+ hidden_states = decoder_layer(
+ hidden_states,
+ attention_mask=causal_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ position_embeddings=position_embeddings,
+ **kwargs,
+ )
+
+ hidden_states = self.norm(hidden_states)
+ return BaseModelOutputWithPast(
+ last_hidden_state=hidden_states,
+ past_key_values=past_key_values if use_cache else None,
+ )
+
+
+class CsmCodebooksHead(nn.Module):
+ def __init__(self, hidden_size, num_codebooks, vocab_size):
+ super().__init__()
+ self.num_codebooks = num_codebooks
+ self.weight = nn.Parameter(torch.empty(self.num_codebooks - 1, hidden_size, vocab_size))
+
+ def forward(self, hidden_states, codebook_indices=None):
+ # -1 because of the concatenated backbone last hidden state
+ codebook_indices = codebook_indices - 1
+ codebook_weight = self.weight[codebook_indices]
+
+ hidden_states = [
+ nn.functional.linear(hidden_states[:, codebook_idx, :], codebook_weight[codebook_idx].T)
+ for codebook_idx in range(codebook_weight.shape[0])
+ ]
+ hidden_states = torch.stack(hidden_states, dim=1)
+
+ return hidden_states
+
+
+@auto_docstring(
+ custom_intro="""
+ The CsmDepthDecoder Model transformer, with a [`CsmCodebooksHead`] on top,
+ which can be seen a position-specific language modeling head, allowing to use a different linear layer for each codebook
+ (e.g. position 0 is the first codebook and uses the first codebook head, etc.)
+ """
+)
+class CsmDepthDecoderForCausalLM(CsmPreTrainedModel, GenerationMixin):
+ _tied_weights_keys = None
+ _tp_plan = None
+ _pp_plan = None
+
+ def __init__(self, config):
+ super().__init__(config)
+ self.model = CsmDepthDecoderModel(config)
+ self.vocab_size = config.vocab_size
+ self.codebooks_head = CsmCodebooksHead(config.hidden_size, config.num_codebooks, config.vocab_size)
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ backbone_last_hidden_state: torch.FloatTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | CausalLMOutputWithPast:
+ r"""
+ backbone_last_hidden_state (`torch.FloatTensor` of shape `(batch_size, backbone_hidden_size)`, *optional*):
+ The last hidden state of the backbone model. Such input is required when the first codebook token (the one generated by the backbone model)
+ is provided in the `input_ids` argument.
+ 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]`.
+ """
+ past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
+ seq_len = inputs_embeds.shape[1] if inputs_embeds is not None else input_ids.shape[1]
+ device = inputs_embeds.device if inputs_embeds is not None else input_ids.device
+ codebook_indices = torch.arange(seq_len, device=device) + past_seen_tokens
+
+ outputs = self.model(
+ input_ids=input_ids,
+ backbone_last_hidden_state=backbone_last_hidden_state,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ hidden_states = outputs[0]
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
+ if isinstance(logits_to_keep, int):
+ if logits_to_keep == 0:
+ # skip idx 0 logits since it's for the concatenated backbone last hidden state
+ slice_indices = slice(1, None)
+ else:
+ slice_indices = slice(-logits_to_keep, None)
+ else:
+ slice_indices = logits_to_keep
+
+ logits = self.codebooks_head(hidden_states[:, slice_indices, :], codebook_indices[slice_indices])
+ logits = logits.contiguous()
+
+ loss = None
+ if labels is not None:
+ shift_labels = labels[..., 1:].contiguous()
+ loss = self.loss_function(
+ logits=logits, labels=None, vocab_size=self.config.vocab_size, shift_labels=shift_labels, **kwargs
+ )
+
+ return CausalLMOutputWithPast(
+ loss=loss,
+ logits=logits,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ )
+
+ def prepare_inputs_for_generation(
+ self,
+ input_ids: torch.LongTensor,
+ next_sequence_length: int | None = None,
+ past_key_values: Cache | None = None,
+ attention_mask: torch.LongTensor | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ is_first_iteration: bool | None = False,
+ **kwargs,
+ ):
+ model_inputs = super().prepare_inputs_for_generation(
+ input_ids, next_sequence_length, past_key_values, attention_mask, inputs_embeds, **kwargs
+ )
+
+ if not is_first_iteration:
+ model_inputs.pop("backbone_last_hidden_state")
+
+ # csm depth decoder does not use position_ids
+ model_inputs.pop("position_ids")
+
+ return model_inputs
+
+
+class CsmBackboneModelEmbeddings(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+ self.embed_audio_tokens = nn.Embedding((config.num_codebooks * config.codebook_size), config.hidden_size)
+ self.register_buffer(
+ "audio_tokens_offsets", torch.arange(config.num_codebooks) * config.codebook_size, persistent=False
+ )
+
+ def forward(self, input_ids):
+ inputs_embeds = self.embed_audio_tokens(input_ids + self.audio_tokens_offsets)
+ inputs_embeds = inputs_embeds.sum(dim=2)
+ return inputs_embeds
+
+
+@auto_docstring
+class CsmBackboneModel(CsmPreTrainedModel):
+ def __init__(self, config):
+ super().__init__(config)
+ self.padding_idx = config.pad_token_id
+ self.vocab_size = config.vocab_size
+ self.embed_tokens = CsmBackboneModelEmbeddings(config)
+ self.layers = nn.ModuleList(
+ [CsmDecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
+ )
+ self.norm = CsmRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.rotary_emb = CsmRotaryEmbedding(config=config)
+ self.gradient_checkpointing = False
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @merge_with_config_defaults
+ @capture_outputs
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ use_cache: bool | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> BaseModelOutputWithPast:
+ r"""
+ input_ids (`torch.LongTensor` of shape `(batch_size, sequence_length, num_codebooks) or (batch_size, sequence_length)`):
+ 1. (batch_size, sequence_length): corresponds to the input sequence prepared with the processor from the text prompt. Such input
+ requires `input_values` to be provided so that audio can be encoded in codebook tokens and then merged with the text tokens.
+
+ 2. (batch_size, sequence_length, num_codebooks): codebook tokens generated during the autoregressive decoding. Such input is not meant to be used by end users.
+
+ Indices can be obtained using [`AutoTokenizer`]. See [`PreTrainedTokenizer.encode`] and
+ [`PreTrainedTokenizer.__call__`] for details.
+
+ [What are input IDs?](../glossary#input-ids)
+ """
+ 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 None:
+ inputs_embeds: torch.Tensor = self.embed_tokens(input_ids)
+
+ if use_cache and past_key_values is None:
+ past_key_values = DynamicCache(config=self.config)
+
+ if position_ids is None:
+ past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
+ position_ids = torch.arange(inputs_embeds.shape[1], device=inputs_embeds.device) + past_seen_tokens
+ position_ids = position_ids.unsqueeze(0)
+
+ causal_mask = create_causal_mask(
+ config=self.config,
+ inputs_embeds=inputs_embeds,
+ attention_mask=attention_mask,
+ past_key_values=past_key_values,
+ position_ids=position_ids,
+ )
+
+ hidden_states = inputs_embeds
+ position_embeddings = self.rotary_emb(hidden_states, position_ids=position_ids)
+
+ for decoder_layer in self.layers[: self.config.num_hidden_layers]:
+ hidden_states = decoder_layer(
+ hidden_states,
+ attention_mask=causal_mask,
+ position_embeddings=position_embeddings,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ hidden_states = self.norm(hidden_states)
+ return BaseModelOutputWithPast(
+ last_hidden_state=hidden_states,
+ past_key_values=past_key_values,
+ )
+
+
+@auto_docstring(
+ custom_intro="""
+ The Csm model consists of two llama-like auto-regressive transformer models: a backbone model that predicts the first codebook token and a depth decoder that predicts the other codebook tokens.
+ """
+)
+class CsmForConditionalGeneration(CsmPreTrainedModel, CsmGenerationMixin):
+ _tied_weights_keys = {
+ "backbone_model.embed_tokens.embed_audio_tokens.weight": "depth_decoder.model.embed_tokens.weight"
+ }
+
+ def __init__(self, config):
+ super().__init__(config)
+ self.vocab_size = config.vocab_size
+ self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
+ self.embed_text_tokens = nn.Embedding(config.text_vocab_size, config.hidden_size)
+ self.backbone_model = CsmBackboneModel._from_config(config)
+ self.depth_decoder = CsmDepthDecoderForCausalLM._from_config(config.depth_decoder_config)
+ self.codec_model = AutoModel.from_config(config.codec_config)
+ self.post_init()
+
+ def get_input_embeddings(self):
+ return self.backbone_model.embed_tokens
+
+ def set_input_embeddings(self, value):
+ self.backbone_model.embed_tokens = value
+
+ @classmethod
+ def from_pretrained(cls, *args, **kwargs):
+ if kwargs.get("output_loading_info", False):
+ model, loading_info = super().from_pretrained(*args, **kwargs)
+ else:
+ model = super().from_pretrained(*args, **kwargs)
+
+ # copy depth decoder generation conf attr to the depth decoder generation config
+ prefix = "depth_decoder_"
+ prefix_len = len(prefix)
+ depth_decoder_attrs = {
+ attr[prefix_len:]: value
+ for attr, value in vars(model.generation_config).items()
+ if attr.startswith(prefix)
+ }
+
+ vars(model.depth_decoder.generation_config).update({"_from_model_config": False, **depth_decoder_attrs})
+
+ # remove the depth decoder generation conf attr from the model generation config
+ for attr in depth_decoder_attrs:
+ delattr(model.generation_config, prefix + attr)
+
+ if "output_loading_info" in kwargs:
+ return model, loading_info
+ else:
+ return model
+
+ def save_pretrained(self, *args, **kwargs):
+ # copy the depth decoder generation config attributes to the model generation config
+ prefix = "depth_decoder_"
+ depth_decoder_attrs = self.depth_decoder.generation_config.to_diff_dict()
+ depth_decoder_attrs.pop("transformers_version", None)
+ for attr, value in depth_decoder_attrs.items():
+ setattr(self.generation_config, prefix + attr, value)
+
+ super().save_pretrained(*args, **kwargs)
+
+ def _merge_input_ids_with_input_values(
+ self,
+ input_ids: torch.Tensor | None = None,
+ input_values: torch.Tensor | None = None,
+ input_values_cutoffs: torch.Tensor | None = None,
+ labels: torch.Tensor | None = None,
+ ) -> torch.Tensor | None:
+ """
+ Merges the input_ids and input_values to produce a single inputs_embeds tensor:
+ 1 - Infers the codec model on the input_values to retrieve codebook token.
+ 2 - Embeds codebook tokens and places them at the correct positions in the inputs_embeds tensor.
+ 3 - If labels are provided, expands them to match codebook dimensions and position the target codebook tokens in the inputs_embeds tensor.
+
+ Args:
+ input_ids (`torch.Tensor` of shape `(batch_size, sequence_length)`):
+ The input ids to embed.
+ input_values (`torch.Tensor` of shape `(batch_size, channels, audio_sequence_length)`):
+ The audio input values to embed.
+ input_values_cutoffs (`torch.Tensor` of shape `(batch_size, max_num_audio)`):
+ The cutoffs of the audio input values relative to its batch index, padded with -1 when no audio.
+ """
+ inputs_embeds = self.embed_text_tokens(input_ids)
+
+ if input_values is not None:
+ # infer input_values_mask
+ input_values_cutoffs = nn.functional.pad(input_values_cutoffs, (1, 0))
+ audio_lengths = input_values_cutoffs[input_values_cutoffs >= 0].diff()
+ audio_lengths = audio_lengths[audio_lengths > 0]
+ input_values_mask = torch.arange(input_values_cutoffs.max(), device=input_values.device).expand(
+ len(audio_lengths), -1
+ )
+ input_values_mask = input_values_mask < audio_lengths.unsqueeze(1)
+
+ # =======================================
+ # TODO: @eustlb, this should be batched !!!
+ # but requires making sure batched inference of the codec model works as intended
+ with torch.no_grad():
+ audio_tokens_list = []
+ for batch_input_values, batch_input_values_cutoffs in zip(input_values, input_values_cutoffs):
+ batch_input_values_cutoffs = batch_input_values_cutoffs[batch_input_values_cutoffs >= 0]
+ for i in range(batch_input_values_cutoffs.shape[0] - 1):
+ start_idx = batch_input_values_cutoffs[i]
+ end_idx = batch_input_values_cutoffs[i + 1]
+ audio_batch = batch_input_values[..., start_idx:end_idx]
+ codec_outputs = self.codec_model.encode(audio_batch.unsqueeze(0))
+ codebook_ids = codec_outputs.audio_codes.transpose(1, -1)
+ audio_tokens_list.append(codebook_ids[0])
+
+ max_audio_frames = max(el.shape[0] for el in audio_tokens_list)
+ batched_audio_token_ids = torch.stack(
+ [nn.functional.pad(el, (0, 0, 0, max_audio_frames - el.shape[0])) for el in audio_tokens_list]
+ )
+ audio_codes_mask = self.codec_model.get_audio_codes_mask(input_values_mask)
+ # =======================================
+ audio_token_id = self.config.audio_token_id
+ audio_token_mask = input_ids == audio_token_id
+
+ audio_embeds = self.backbone_model.embed_tokens(batched_audio_token_ids)
+ inputs_embeds[audio_token_mask] = audio_embeds[audio_codes_mask]
+
+ # same for the audio eos token
+ audio_eos_frame_ids = (
+ torch.ones((1, 1, self.config.num_codebooks), device=input_ids.device, dtype=torch.long)
+ * self.config.codebook_eos_token_id
+ )
+ audio_eos_embeds = self.backbone_model.embed_tokens(audio_eos_frame_ids).squeeze(1)
+
+ audio_eos_token_mask = input_ids == self.config.audio_eos_token_id
+ inputs_embeds[audio_eos_token_mask] = audio_eos_embeds.repeat(audio_eos_token_mask.sum(), 1)
+
+ # if the labels are provided, we need to expand the labels to (batch_size, seq_length, num_codebooks)
+ if labels is not None:
+ labels_expanded = labels.unsqueeze(-1).repeat(1, 1, self.config.num_codebooks)
+ labels_expanded[audio_token_mask] = batched_audio_token_ids[audio_codes_mask]
+ labels_expanded[audio_eos_token_mask] = audio_eos_frame_ids
+ # mask depth decoder
+ depth_decoder_ignore_frames_idxs = (labels == -101).nonzero(as_tuple=True)
+ labels_expanded[depth_decoder_ignore_frames_idxs[0], depth_decoder_ignore_frames_idxs[1], 1:] = -100
+ labels = labels_expanded
+
+ return {"inputs_embeds": inputs_embeds, "labels": labels}
+
+ def prepare_inputs_for_generation(
+ self,
+ input_ids: torch.LongTensor,
+ next_sequence_length: int | None = None,
+ past_key_values: Cache | None = None,
+ attention_mask: torch.LongTensor | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ **kwargs,
+ ):
+ model_inputs = super().prepare_inputs_for_generation(
+ input_ids=input_ids,
+ next_sequence_length=next_sequence_length,
+ past_key_values=past_key_values,
+ attention_mask=attention_mask,
+ inputs_embeds=inputs_embeds,
+ **kwargs,
+ )
+
+ if input_ids is not None and input_ids.ndim == 2 and model_inputs.get("inputs_embeds") is None:
+ merged_inputs = self._merge_input_ids_with_input_values(
+ input_ids=input_ids,
+ input_values=kwargs.get("input_values"),
+ input_values_cutoffs=kwargs.get("input_values_cutoffs"),
+ labels=kwargs.get("labels"),
+ )
+ model_inputs.update(
+ {"inputs_embeds": merged_inputs["inputs_embeds"], "labels": merged_inputs["labels"], "input_ids": None}
+ )
+
+ return model_inputs
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ input_values: torch.Tensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ input_values_cutoffs: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | CsmOutputWithPast:
+ r"""
+ input_ids (`torch.LongTensor` of shape `(batch_size, sequence_length, num_codebooks) or (batch_size, sequence_length)`):
+ 1. (batch_size, sequence_length): corresponds to the input sequence prepared with the processor from the text prompt. Such input
+ requires `input_values` to be provided so that audio can be encoded in codebook tokens and then merged with the text tokens.
+
+ 2. (batch_size, sequence_length, num_codebooks): codebook tokens generated during the autoregressive decoding. Such input is not meant to be used by end users.
+
+ Indices can be obtained using [`AutoTokenizer`]. See [`PreTrainedTokenizer.encode`] and
+ [`PreTrainedTokenizer.__call__`] for details.
+
+ [What are input IDs?](../glossary#input-ids)
+ input_values_cutoffs (`torch.Tensor` of shape `(batch_size, max_num_audio)`, *optional*):
+ Specify the end positions of audio segments within each batch entry, relative to the concatenated audio input.
+ If a batch entry has fewer segments than the maximum, it is padded with -1. For example, in a batch of 2 sequences
+ where the first contains 2 audio segments of length l1, and the second contains 1 audio segment of length l2,
+ the input_values_cutoffs would be: [[l1, 2 * l1], [l2, -1]].
+ labels (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*):
+ Labels for computing the masked language modeling loss. Indices should be in `[config.audio_token_id, -100, -101]`.
+ Requires targeted `input_values` to be provided as audio tokens will be inferred from it using the `codec_model`.
+ - `config.audio_token_id` indicates an audio frames (considering sequence length elements as frames)
+ - `-100` will be ignored in the loss computation
+ - `-101` indicates the audio frame will be used only for the backbone model (using the first codebook token as labels)
+
+ Such labels can be prepared using `output_labels=True` when calling [`CsmProcessor`].
+ logits_to_keep (`int` or `torch.Tensor`, *optional*):
+ Kept for compatibility. Does not support another value than:
+ 1. `0`, which is equivalent to keeping all logits, used in the training regime
+ 2. `1`, which is equivalent to keeping only the last logit, used in the generation regime
+
+ Example:
+
+ ```python
+ >>> import torch
+ >>> from transformers import CsmForConditionalGeneration, AutoProcessor
+ >>> from datasets import load_dataset, Audio
+
+ >>> model_id = "sesame/csm-1b"
+ >>> torch_device = "cuda" if torch.cuda.is_available() else "cpu"
+
+ >>> processor = AutoProcessor.from_pretrained(model_id)
+
+ >>> ds = load_dataset("hf-internal-testing/dailytalk-dummy", split="train")
+ >>> # ensure the audio is 24kHz
+ >>> ds = ds.cast_column("audio", Audio(sampling_rate=24000))
+
+ >>> conversation = []
+ >>> # prepare a conversation with text and corresponding audio
+ >>> for text, audio, speaker_id in zip(ds[:4]["text"], ds[:4]["audio"], ds[:4]["speaker_id"]):
+ ... conversation.append(
+ ... {
+ ... "role": f"{speaker_id}",
+ ... "content": [{"type": "text", "text": text}, {"type": "audio", "path": audio["array"]}],
+ ... }
+ ... )
+
+ >>> inputs = processor.apply_chat_template(
+ ... conversation,
+ ... tokenize=True,
+ ... return_dict=True,
+ ... output_labels=True,
+ ... ).to(torch_device)
+
+ >>> model = CsmForConditionalGeneration.from_pretrained(model_id, device_map=torch_device)
+ >>> output = model(**inputs)
+ >>> output.loss.backward()
+ ```"""
+ if input_ids is not None and input_ids.ndim == 2:
+ merged_inputs = self._merge_input_ids_with_input_values(
+ input_ids, input_values, input_values_cutoffs, labels
+ )
+ inputs_embeds = merged_inputs["inputs_embeds"]
+ labels = merged_inputs["labels"]
+ input_ids = None
+
+ backbone_outputs = self.backbone_model(
+ input_ids=input_ids,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ backbone_hidden_states = backbone_outputs[0]
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
+ backbone_logits = self.lm_head(backbone_hidden_states[:, slice_indices, :])
+
+ loss = None
+ backbone_loss = None
+ depth_decoder_loss = None
+ depth_decoder_outputs = None
+ if labels is not None:
+ # select first codebook as labels for the backbone model
+ backbone_labels = labels[:, :, 0]
+ backbone_loss = self.loss_function(
+ logits=backbone_logits, labels=backbone_labels, vocab_size=self.config.vocab_size, **kwargs
+ )
+
+ # for the depth decoder, we need to select the frames to train on
+ # those are frames where the label is not uniformly `ignore_index` along the codebook dimension
+ train_mask = ~(labels[:, :, 1:] == -100).all(dim=-1)
+ depth_decoder_input_ids = labels[train_mask][..., : self.config.num_codebooks - 1]
+ # add place holder in position 0 that will be replaced by the backbone_last_hidden_state
+ depth_decoder_input_ids = nn.functional.pad(depth_decoder_input_ids, (1, 0), value=0)
+
+ train_idxs = train_mask.nonzero(as_tuple=True)
+ backbone_last_hidden_states = backbone_hidden_states[train_idxs[0], train_idxs[1] - 1, :]
+ depth_decoder_labels = labels[train_mask]
+
+ depth_decoder_outputs = self.depth_decoder(
+ input_ids=depth_decoder_input_ids,
+ backbone_last_hidden_state=backbone_last_hidden_states,
+ use_cache=use_cache,
+ return_dict=True,
+ labels=depth_decoder_labels,
+ **kwargs,
+ )
+
+ depth_decoder_loss = depth_decoder_outputs.loss
+ loss = backbone_loss + depth_decoder_loss
+
+ return CsmOutputWithPast(
+ loss=loss,
+ backbone_loss=backbone_loss,
+ depth_decoder_loss=depth_decoder_loss,
+ logits=backbone_logits,
+ past_key_values=backbone_outputs.past_key_values,
+ hidden_states=backbone_outputs.hidden_states,
+ attentions=backbone_outputs.attentions,
+ depth_decoder_logits=depth_decoder_outputs.logits if depth_decoder_outputs is not None else None,
+ depth_decoder_past_key_values=depth_decoder_outputs.past_key_values
+ if depth_decoder_outputs is not None
+ else None,
+ depth_decoder_hidden_states=depth_decoder_outputs.hidden_states
+ if depth_decoder_outputs is not None
+ else None,
+ depth_decoder_attentions=depth_decoder_outputs.attentions if depth_decoder_outputs is not None else None,
+ )
+
+
+__all__ = [
+ "CsmPreTrainedModel",
+ "CsmBackboneModel",
+ "CsmDepthDecoderModel",
+ "CsmDepthDecoderForCausalLM",
+ "CsmForConditionalGeneration",
+]
diff --git a/third_party/transformers/src/transformers/models/csm/modular_csm.py b/third_party/transformers/src/transformers/models/csm/modular_csm.py
new file mode 100644
index 0000000000000000000000000000000000000000..8ba8bc66dad3700ca76948104c357e4c8020316b
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/csm/modular_csm.py
@@ -0,0 +1,756 @@
+# Copyright 2025 Sesame and The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from dataclasses import dataclass
+
+import torch
+import torch.nn as nn
+
+from ... import initialization as init
+from ...cache_utils import Cache, DynamicCache
+from ...generation import GenerationMixin
+from ...masking_utils import create_causal_mask
+from ...modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast
+from ...modeling_utils import PreTrainedModel
+from ...processing_utils import Unpack
+from ...utils import ModelOutput, auto_docstring, can_return_tuple, logging
+from ...utils.generic import merge_with_config_defaults
+from ...utils.import_utils import is_torchdynamo_compiling
+from ...utils.output_capturing import capture_outputs
+from ..auto import AutoModel
+from ..llama.modeling_llama import (
+ LlamaAttention,
+ LlamaDecoderLayer,
+ LlamaForCausalLM,
+ LlamaMLP,
+ LlamaModel,
+ LlamaRMSNorm,
+ LlamaRotaryEmbedding,
+ TransformersKwargs,
+)
+from .configuration_csm import CsmConfig, CsmDepthDecoderConfig
+from .generation_csm import CsmGenerationMixin
+
+
+logger = logging.get_logger(__name__)
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ Base class for the model autoregressive outputs.
+ """
+)
+class CsmOutputWithPast(ModelOutput):
+ r"""
+ loss (`torch.FloatTensor` of shape `(1,)`, *optional*, returned when `labels` is provided):
+ Language modeling loss (for next-token prediction).
+ logits (`torch.FloatTensor` of shape `(batch_size, sequence_length, config.vocab_size)`):
+ Prediction scores of the language modeling head (scores for each vocabulary token before SoftMax).
+ past_key_values (`Cache`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
+ It is a [`~cache_utils.Cache`] instance. For more details, see our [kv cache guide](https://huggingface.co/docs/transformers/en/kv_cache).
+
+ Contains pre-computed hidden-states (key and values in the self-attention blocks) that can be used (see
+ `past_key_values` input) to speed up sequential decoding.
+ depth_decoder_loss (`torch.FloatTensor` of shape `(1,)`, *optional*, returned when `labels` is provided):
+ Language modeling loss (for next-token prediction) of the depth decoder model.
+ depth_decoder_logits (`torch.FloatTensor` of shape `(batch_size, sequence_length, config.vocab_size)`):
+ Prediction scores of the depth decoder (scores for each vocabulary token before SoftMax).
+ depth_decoder_past_key_values (`Cache`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
+ It is a [`~cache_utils.Cache`] instance. For more details, see our [kv cache guide](https://huggingface.co/docs/transformers/en/kv_cache).
+ depth_decoder_hidden_states (`tuple(torch.FloatTensor)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
+ Tuple of `torch.FloatTensor` (one for the output of the embeddings, if the model has an embedding layer, +
+ one for the output of each layer) of shape `(batch_size, sequence_length, hidden_size)`.
+
+ Hidden-states of the model at the output of each layer plus the optional initial embedding outputs.
+ depth_decoder_attentions (`tuple(torch.FloatTensor)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`):
+ Tuple of `torch.FloatTensor` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
+ sequence_length)`.
+ backbone_loss (`torch.FloatTensor` of shape `(1,)`, *optional*, returned when `labels` is provided):
+ Language modeling loss (for next-token prediction) of the backbone model.
+ """
+
+ loss: torch.FloatTensor | None = None
+ logits: torch.FloatTensor | None = None
+ past_key_values: Cache | None = None
+ hidden_states: tuple[torch.FloatTensor, ...] | None = None
+ attentions: tuple[torch.FloatTensor, ...] | None = None
+ depth_decoder_loss: torch.FloatTensor | None = None
+ depth_decoder_logits: torch.FloatTensor | None = None
+ depth_decoder_past_key_values: Cache | None = None
+ depth_decoder_hidden_states: tuple[torch.FloatTensor, ...] | None = None
+ depth_decoder_attentions: tuple[torch.FloatTensor, ...] | None = None
+ backbone_loss: torch.FloatTensor | None = None
+
+
+# manually specify names for correct naming when converting from modular
+class CsmRMSNorm(LlamaRMSNorm):
+ pass
+
+
+class CsmRotaryEmbedding(LlamaRotaryEmbedding):
+ pass
+
+
+class CsmMLP(LlamaMLP):
+ pass
+
+
+class CsmAttention(LlamaAttention):
+ pass
+
+
+class CsmDecoderLayer(LlamaDecoderLayer):
+ pass
+
+
+@auto_docstring(
+ custom_intro="""
+ The bare Csm Model outputting raw hidden-states without any specific head on top.
+ """
+)
+@auto_docstring
+class CsmPreTrainedModel(PreTrainedModel):
+ config: CsmConfig
+ base_model_prefix = "model"
+ input_modalities = ("audio", "text")
+ supports_gradient_checkpointing = True
+ _no_split_modules = ["CsmDecoderLayer"]
+ _skip_keys_device_placement = ["past_key_values"]
+ _supports_flash_attn = True
+ _supports_sdpa = True
+ # does not because of Mimi codec model
+ # _supports_flex_attn = True
+
+ _can_compile_fullgraph = True
+ _supports_attention_backend = True
+ _can_record_outputs = {
+ "hidden_states": CsmDecoderLayer,
+ "attentions": CsmAttention,
+ }
+
+ @torch.no_grad()
+ def _init_weights(self, module):
+ super()._init_weights(module)
+ if isinstance(module, CsmCodebooksHead):
+ num_codebooks = module.num_codebooks
+ for i in range(num_codebooks - 1):
+ init.normal_(module.weight, mean=0.0, std=self.config.initializer_range)
+ elif isinstance(module, CsmBackboneModelEmbeddings):
+ init.copy_(module.audio_tokens_offsets, torch.arange(self.config.num_codebooks) * self.config.vocab_size)
+
+
+@auto_docstring
+class CsmDepthDecoderModel(LlamaModel, CsmPreTrainedModel):
+ config: CsmDepthDecoderConfig
+
+ def __init__(self, config):
+ super().__init__(config)
+ self.embed_tokens = nn.Embedding((config.num_codebooks * config.vocab_size), config.backbone_hidden_size)
+ self.inputs_embeds_projector = nn.Linear(config.backbone_hidden_size, config.hidden_size, bias=False)
+
+ @merge_with_config_defaults
+ @capture_outputs
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ backbone_last_hidden_state: torch.FloatTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ use_cache: bool | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | BaseModelOutputWithPast:
+ r"""
+ backbone_last_hidden_state (`torch.FloatTensor` of shape `(batch_size, backbone_hidden_size)`, *optional*):
+ The last hidden state of the backbone model. Such input is required when the first codebook token (the one generated by the backbone model)
+ is provided in the `input_ids` argument.
+ """
+ if position_ids is not None and not is_torchdynamo_compiling():
+ logger.warning_once(
+ "Custom `position_ids` were provided but will be ignored. CSM depth decoder automatically determines position_ids "
+ "and as it requires them to be identical across the batch, the provided position_ids will be ignored."
+ )
+ position_ids = None
+ if (input_ids is None) ^ (inputs_embeds is not None):
+ raise ValueError("You must specify exactly one of input_ids or inputs_embeds.")
+
+ if use_cache and past_key_values is None:
+ past_key_values = DynamicCache(config=self.config)
+
+ past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
+ inputs_seq_length = inputs_embeds.shape[1] if inputs_embeds is not None else input_ids.shape[1]
+ device = inputs_embeds.device if inputs_embeds is not None else input_ids.device
+ position_ids = torch.arange(past_seen_tokens, past_seen_tokens + inputs_seq_length, device=device)
+
+ if inputs_embeds is None:
+ codebook_idxs = torch.clamp(position_ids - 1, min=0)
+ offset = codebook_idxs * self.vocab_size
+ inputs_embeds = self.embed_tokens(input_ids + offset)
+
+ input_ids_are_first_codebook = position_ids[0] == 0
+ if backbone_last_hidden_state is not None:
+ inputs_embeds[:, 0] = backbone_last_hidden_state
+ else:
+ if not is_torchdynamo_compiling() and input_ids_are_first_codebook:
+ logger.warning(
+ "When the first codebook token is provided, `backbone_last_hidden_state` should also be provided for correct inference."
+ )
+
+ inputs_embeds = self.inputs_embeds_projector(inputs_embeds)
+
+ causal_mask = create_causal_mask(
+ config=self.config,
+ inputs_embeds=inputs_embeds,
+ attention_mask=attention_mask,
+ past_key_values=past_key_values,
+ position_ids=position_ids,
+ )
+
+ hidden_states = inputs_embeds
+
+ # create position embeddings to be shared across the decoder layers
+ position_ids = position_ids.unsqueeze(0)
+ position_embeddings = self.rotary_emb(hidden_states, position_ids=position_ids)
+
+ for decoder_layer in self.layers[: self.config.num_hidden_layers]:
+ hidden_states = decoder_layer(
+ hidden_states,
+ attention_mask=causal_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ position_embeddings=position_embeddings,
+ **kwargs,
+ )
+
+ hidden_states = self.norm(hidden_states)
+ return BaseModelOutputWithPast(
+ last_hidden_state=hidden_states,
+ past_key_values=past_key_values if use_cache else None,
+ )
+
+
+class CsmCodebooksHead(nn.Module):
+ def __init__(self, hidden_size, num_codebooks, vocab_size):
+ super().__init__()
+ self.num_codebooks = num_codebooks
+ self.weight = nn.Parameter(torch.empty(self.num_codebooks - 1, hidden_size, vocab_size))
+
+ def forward(self, hidden_states, codebook_indices=None):
+ # -1 because of the concatenated backbone last hidden state
+ codebook_indices = codebook_indices - 1
+ codebook_weight = self.weight[codebook_indices]
+
+ hidden_states = [
+ nn.functional.linear(hidden_states[:, codebook_idx, :], codebook_weight[codebook_idx].T)
+ for codebook_idx in range(codebook_weight.shape[0])
+ ]
+ hidden_states = torch.stack(hidden_states, dim=1)
+
+ return hidden_states
+
+
+@auto_docstring(
+ custom_intro="""
+ The CsmDepthDecoder Model transformer, with a [`CsmCodebooksHead`] on top,
+ which can be seen a position-specific language modeling head, allowing to use a different linear layer for each codebook
+ (e.g. position 0 is the first codebook and uses the first codebook head, etc.)
+ """
+)
+class CsmDepthDecoderForCausalLM(LlamaForCausalLM, GenerationMixin):
+ _tied_weights_keys = None
+ _tp_plan = None
+ _pp_plan = None
+
+ def __init__(self, config):
+ super().__init__(config)
+ del self.lm_head
+ self.codebooks_head = CsmCodebooksHead(config.hidden_size, config.num_codebooks, config.vocab_size)
+ self.model = CsmDepthDecoderModel(config)
+
+ def prepare_inputs_for_generation(
+ self,
+ input_ids: torch.LongTensor,
+ next_sequence_length: int | None = None,
+ past_key_values: Cache | None = None,
+ attention_mask: torch.LongTensor | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ is_first_iteration: bool | None = False,
+ **kwargs,
+ ):
+ model_inputs = super().prepare_inputs_for_generation(
+ input_ids, next_sequence_length, past_key_values, attention_mask, inputs_embeds, **kwargs
+ )
+
+ if not is_first_iteration:
+ model_inputs.pop("backbone_last_hidden_state")
+
+ # csm depth decoder does not use position_ids
+ model_inputs.pop("position_ids")
+
+ return model_inputs
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ backbone_last_hidden_state: torch.FloatTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | CausalLMOutputWithPast:
+ r"""
+ backbone_last_hidden_state (`torch.FloatTensor` of shape `(batch_size, backbone_hidden_size)`, *optional*):
+ The last hidden state of the backbone model. Such input is required when the first codebook token (the one generated by the backbone model)
+ is provided in the `input_ids` argument.
+ 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]`.
+ """
+ past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
+ seq_len = inputs_embeds.shape[1] if inputs_embeds is not None else input_ids.shape[1]
+ device = inputs_embeds.device if inputs_embeds is not None else input_ids.device
+ codebook_indices = torch.arange(seq_len, device=device) + past_seen_tokens
+
+ outputs = self.model(
+ input_ids=input_ids,
+ backbone_last_hidden_state=backbone_last_hidden_state,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ hidden_states = outputs[0]
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
+ if isinstance(logits_to_keep, int):
+ if logits_to_keep == 0:
+ # skip idx 0 logits since it's for the concatenated backbone last hidden state
+ slice_indices = slice(1, None)
+ else:
+ slice_indices = slice(-logits_to_keep, None)
+ else:
+ slice_indices = logits_to_keep
+
+ logits = self.codebooks_head(hidden_states[:, slice_indices, :], codebook_indices[slice_indices])
+ logits = logits.contiguous()
+
+ loss = None
+ if labels is not None:
+ shift_labels = labels[..., 1:].contiguous()
+ loss = self.loss_function(
+ logits=logits, labels=None, vocab_size=self.config.vocab_size, shift_labels=shift_labels, **kwargs
+ )
+
+ return CausalLMOutputWithPast(
+ loss=loss,
+ logits=logits,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ )
+
+
+class CsmBackboneModelEmbeddings(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+ self.embed_audio_tokens = nn.Embedding((config.num_codebooks * config.codebook_size), config.hidden_size)
+ self.register_buffer(
+ "audio_tokens_offsets", torch.arange(config.num_codebooks) * config.codebook_size, persistent=False
+ )
+
+ def forward(self, input_ids):
+ inputs_embeds = self.embed_audio_tokens(input_ids + self.audio_tokens_offsets)
+ inputs_embeds = inputs_embeds.sum(dim=2)
+ return inputs_embeds
+
+
+@auto_docstring
+class CsmBackboneModel(LlamaModel):
+ def __init__(self, config):
+ super().__init__(config)
+ self.embed_tokens = CsmBackboneModelEmbeddings(config)
+
+ @merge_with_config_defaults
+ @capture_outputs
+ @auto_docstring
+ def forward(self, **super_kwargs):
+ r"""
+ input_ids (`torch.LongTensor` of shape `(batch_size, sequence_length, num_codebooks) or (batch_size, sequence_length)`):
+ 1. (batch_size, sequence_length): corresponds to the input sequence prepared with the processor from the text prompt. Such input
+ requires `input_values` to be provided so that audio can be encoded in codebook tokens and then merged with the text tokens.
+
+ 2. (batch_size, sequence_length, num_codebooks): codebook tokens generated during the autoregressive decoding. Such input is not meant to be used by end users.
+
+ Indices can be obtained using [`AutoTokenizer`]. See [`PreTrainedTokenizer.encode`] and
+ [`PreTrainedTokenizer.__call__`] for details.
+
+ [What are input IDs?](../glossary#input-ids)
+ """
+ return super().forward(**super_kwargs)
+
+
+@auto_docstring(
+ custom_intro="""
+ The Csm model consists of two llama-like auto-regressive transformer models: a backbone model that predicts the first codebook token and a depth decoder that predicts the other codebook tokens.
+ """
+)
+class CsmForConditionalGeneration(CsmPreTrainedModel, CsmGenerationMixin):
+ _tied_weights_keys = {
+ "backbone_model.embed_tokens.embed_audio_tokens.weight": "depth_decoder.model.embed_tokens.weight"
+ }
+
+ def __init__(self, config):
+ super().__init__(config)
+ self.vocab_size = config.vocab_size
+ self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
+ self.embed_text_tokens = nn.Embedding(config.text_vocab_size, config.hidden_size)
+ self.backbone_model = CsmBackboneModel._from_config(config)
+ self.depth_decoder = CsmDepthDecoderForCausalLM._from_config(config.depth_decoder_config)
+ self.codec_model = AutoModel.from_config(config.codec_config)
+ self.post_init()
+
+ def get_input_embeddings(self):
+ return self.backbone_model.embed_tokens
+
+ def set_input_embeddings(self, value):
+ self.backbone_model.embed_tokens = value
+
+ @classmethod
+ def from_pretrained(cls, *args, **kwargs):
+ if kwargs.get("output_loading_info", False):
+ model, loading_info = super().from_pretrained(*args, **kwargs)
+ else:
+ model = super().from_pretrained(*args, **kwargs)
+
+ # copy depth decoder generation conf attr to the depth decoder generation config
+ prefix = "depth_decoder_"
+ prefix_len = len(prefix)
+ depth_decoder_attrs = {
+ attr[prefix_len:]: value
+ for attr, value in vars(model.generation_config).items()
+ if attr.startswith(prefix)
+ }
+
+ vars(model.depth_decoder.generation_config).update({"_from_model_config": False, **depth_decoder_attrs})
+
+ # remove the depth decoder generation conf attr from the model generation config
+ for attr in depth_decoder_attrs:
+ delattr(model.generation_config, prefix + attr)
+
+ if "output_loading_info" in kwargs:
+ return model, loading_info
+ else:
+ return model
+
+ def save_pretrained(self, *args, **kwargs):
+ # copy the depth decoder generation config attributes to the model generation config
+ prefix = "depth_decoder_"
+ depth_decoder_attrs = self.depth_decoder.generation_config.to_diff_dict()
+ depth_decoder_attrs.pop("transformers_version", None)
+ for attr, value in depth_decoder_attrs.items():
+ setattr(self.generation_config, prefix + attr, value)
+
+ super().save_pretrained(*args, **kwargs)
+
+ def _merge_input_ids_with_input_values(
+ self,
+ input_ids: torch.Tensor | None = None,
+ input_values: torch.Tensor | None = None,
+ input_values_cutoffs: torch.Tensor | None = None,
+ labels: torch.Tensor | None = None,
+ ) -> torch.Tensor | None:
+ """
+ Merges the input_ids and input_values to produce a single inputs_embeds tensor:
+ 1 - Infers the codec model on the input_values to retrieve codebook token.
+ 2 - Embeds codebook tokens and places them at the correct positions in the inputs_embeds tensor.
+ 3 - If labels are provided, expands them to match codebook dimensions and position the target codebook tokens in the inputs_embeds tensor.
+
+ Args:
+ input_ids (`torch.Tensor` of shape `(batch_size, sequence_length)`):
+ The input ids to embed.
+ input_values (`torch.Tensor` of shape `(batch_size, channels, audio_sequence_length)`):
+ The audio input values to embed.
+ input_values_cutoffs (`torch.Tensor` of shape `(batch_size, max_num_audio)`):
+ The cutoffs of the audio input values relative to its batch index, padded with -1 when no audio.
+ """
+ inputs_embeds = self.embed_text_tokens(input_ids)
+
+ if input_values is not None:
+ # infer input_values_mask
+ input_values_cutoffs = nn.functional.pad(input_values_cutoffs, (1, 0))
+ audio_lengths = input_values_cutoffs[input_values_cutoffs >= 0].diff()
+ audio_lengths = audio_lengths[audio_lengths > 0]
+ input_values_mask = torch.arange(input_values_cutoffs.max(), device=input_values.device).expand(
+ len(audio_lengths), -1
+ )
+ input_values_mask = input_values_mask < audio_lengths.unsqueeze(1)
+
+ # =======================================
+ # TODO: @eustlb, this should be batched !!!
+ # but requires making sure batched inference of the codec model works as intended
+ with torch.no_grad():
+ audio_tokens_list = []
+ for batch_input_values, batch_input_values_cutoffs in zip(input_values, input_values_cutoffs):
+ batch_input_values_cutoffs = batch_input_values_cutoffs[batch_input_values_cutoffs >= 0]
+ for i in range(batch_input_values_cutoffs.shape[0] - 1):
+ start_idx = batch_input_values_cutoffs[i]
+ end_idx = batch_input_values_cutoffs[i + 1]
+ audio_batch = batch_input_values[..., start_idx:end_idx]
+ codec_outputs = self.codec_model.encode(audio_batch.unsqueeze(0))
+ codebook_ids = codec_outputs.audio_codes.transpose(1, -1)
+ audio_tokens_list.append(codebook_ids[0])
+
+ max_audio_frames = max(el.shape[0] for el in audio_tokens_list)
+ batched_audio_token_ids = torch.stack(
+ [nn.functional.pad(el, (0, 0, 0, max_audio_frames - el.shape[0])) for el in audio_tokens_list]
+ )
+ audio_codes_mask = self.codec_model.get_audio_codes_mask(input_values_mask)
+ # =======================================
+ audio_token_id = self.config.audio_token_id
+ audio_token_mask = input_ids == audio_token_id
+
+ audio_embeds = self.backbone_model.embed_tokens(batched_audio_token_ids)
+ inputs_embeds[audio_token_mask] = audio_embeds[audio_codes_mask]
+
+ # same for the audio eos token
+ audio_eos_frame_ids = (
+ torch.ones((1, 1, self.config.num_codebooks), device=input_ids.device, dtype=torch.long)
+ * self.config.codebook_eos_token_id
+ )
+ audio_eos_embeds = self.backbone_model.embed_tokens(audio_eos_frame_ids).squeeze(1)
+
+ audio_eos_token_mask = input_ids == self.config.audio_eos_token_id
+ inputs_embeds[audio_eos_token_mask] = audio_eos_embeds.repeat(audio_eos_token_mask.sum(), 1)
+
+ # if the labels are provided, we need to expand the labels to (batch_size, seq_length, num_codebooks)
+ if labels is not None:
+ labels_expanded = labels.unsqueeze(-1).repeat(1, 1, self.config.num_codebooks)
+ labels_expanded[audio_token_mask] = batched_audio_token_ids[audio_codes_mask]
+ labels_expanded[audio_eos_token_mask] = audio_eos_frame_ids
+ # mask depth decoder
+ depth_decoder_ignore_frames_idxs = (labels == -101).nonzero(as_tuple=True)
+ labels_expanded[depth_decoder_ignore_frames_idxs[0], depth_decoder_ignore_frames_idxs[1], 1:] = -100
+ labels = labels_expanded
+
+ return {"inputs_embeds": inputs_embeds, "labels": labels}
+
+ def prepare_inputs_for_generation(
+ self,
+ input_ids: torch.LongTensor,
+ next_sequence_length: int | None = None,
+ past_key_values: Cache | None = None,
+ attention_mask: torch.LongTensor | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ **kwargs,
+ ):
+ model_inputs = super().prepare_inputs_for_generation(
+ input_ids=input_ids,
+ next_sequence_length=next_sequence_length,
+ past_key_values=past_key_values,
+ attention_mask=attention_mask,
+ inputs_embeds=inputs_embeds,
+ **kwargs,
+ )
+
+ if input_ids is not None and input_ids.ndim == 2 and model_inputs.get("inputs_embeds") is None:
+ merged_inputs = self._merge_input_ids_with_input_values(
+ input_ids=input_ids,
+ input_values=kwargs.get("input_values"),
+ input_values_cutoffs=kwargs.get("input_values_cutoffs"),
+ labels=kwargs.get("labels"),
+ )
+ model_inputs.update(
+ {"inputs_embeds": merged_inputs["inputs_embeds"], "labels": merged_inputs["labels"], "input_ids": None}
+ )
+
+ return model_inputs
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ input_values: torch.Tensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ input_values_cutoffs: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | CsmOutputWithPast:
+ r"""
+ input_ids (`torch.LongTensor` of shape `(batch_size, sequence_length, num_codebooks) or (batch_size, sequence_length)`):
+ 1. (batch_size, sequence_length): corresponds to the input sequence prepared with the processor from the text prompt. Such input
+ requires `input_values` to be provided so that audio can be encoded in codebook tokens and then merged with the text tokens.
+
+ 2. (batch_size, sequence_length, num_codebooks): codebook tokens generated during the autoregressive decoding. Such input is not meant to be used by end users.
+
+ Indices can be obtained using [`AutoTokenizer`]. See [`PreTrainedTokenizer.encode`] and
+ [`PreTrainedTokenizer.__call__`] for details.
+
+ [What are input IDs?](../glossary#input-ids)
+ input_values_cutoffs (`torch.Tensor` of shape `(batch_size, max_num_audio)`, *optional*):
+ Specify the end positions of audio segments within each batch entry, relative to the concatenated audio input.
+ If a batch entry has fewer segments than the maximum, it is padded with -1. For example, in a batch of 2 sequences
+ where the first contains 2 audio segments of length l1, and the second contains 1 audio segment of length l2,
+ the input_values_cutoffs would be: [[l1, 2 * l1], [l2, -1]].
+ labels (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*):
+ Labels for computing the masked language modeling loss. Indices should be in `[config.audio_token_id, -100, -101]`.
+ Requires targeted `input_values` to be provided as audio tokens will be inferred from it using the `codec_model`.
+ - `config.audio_token_id` indicates an audio frames (considering sequence length elements as frames)
+ - `-100` will be ignored in the loss computation
+ - `-101` indicates the audio frame will be used only for the backbone model (using the first codebook token as labels)
+
+ Such labels can be prepared using `output_labels=True` when calling [`CsmProcessor`].
+ logits_to_keep (`int` or `torch.Tensor`, *optional*):
+ Kept for compatibility. Does not support another value than:
+ 1. `0`, which is equivalent to keeping all logits, used in the training regime
+ 2. `1`, which is equivalent to keeping only the last logit, used in the generation regime
+
+ Example:
+
+ ```python
+ >>> import torch
+ >>> from transformers import CsmForConditionalGeneration, AutoProcessor
+ >>> from datasets import load_dataset, Audio
+
+ >>> model_id = "sesame/csm-1b"
+ >>> torch_device = "cuda" if torch.cuda.is_available() else "cpu"
+
+ >>> processor = AutoProcessor.from_pretrained(model_id)
+
+ >>> ds = load_dataset("hf-internal-testing/dailytalk-dummy", split="train")
+ >>> # ensure the audio is 24kHz
+ >>> ds = ds.cast_column("audio", Audio(sampling_rate=24000))
+
+ >>> conversation = []
+ >>> # prepare a conversation with text and corresponding audio
+ >>> for text, audio, speaker_id in zip(ds[:4]["text"], ds[:4]["audio"], ds[:4]["speaker_id"]):
+ ... conversation.append(
+ ... {
+ ... "role": f"{speaker_id}",
+ ... "content": [{"type": "text", "text": text}, {"type": "audio", "path": audio["array"]}],
+ ... }
+ ... )
+
+ >>> inputs = processor.apply_chat_template(
+ ... conversation,
+ ... tokenize=True,
+ ... return_dict=True,
+ ... output_labels=True,
+ ... ).to(torch_device)
+
+ >>> model = CsmForConditionalGeneration.from_pretrained(model_id, device_map=torch_device)
+ >>> output = model(**inputs)
+ >>> output.loss.backward()
+ ```"""
+ if input_ids is not None and input_ids.ndim == 2:
+ merged_inputs = self._merge_input_ids_with_input_values(
+ input_ids, input_values, input_values_cutoffs, labels
+ )
+ inputs_embeds = merged_inputs["inputs_embeds"]
+ labels = merged_inputs["labels"]
+ input_ids = None
+
+ backbone_outputs = self.backbone_model(
+ input_ids=input_ids,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ backbone_hidden_states = backbone_outputs[0]
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
+ backbone_logits = self.lm_head(backbone_hidden_states[:, slice_indices, :])
+
+ loss = None
+ backbone_loss = None
+ depth_decoder_loss = None
+ depth_decoder_outputs = None
+ if labels is not None:
+ # select first codebook as labels for the backbone model
+ backbone_labels = labels[:, :, 0]
+ backbone_loss = self.loss_function(
+ logits=backbone_logits, labels=backbone_labels, vocab_size=self.config.vocab_size, **kwargs
+ )
+
+ # for the depth decoder, we need to select the frames to train on
+ # those are frames where the label is not uniformly `ignore_index` along the codebook dimension
+ train_mask = ~(labels[:, :, 1:] == -100).all(dim=-1)
+ depth_decoder_input_ids = labels[train_mask][..., : self.config.num_codebooks - 1]
+ # add place holder in position 0 that will be replaced by the backbone_last_hidden_state
+ depth_decoder_input_ids = nn.functional.pad(depth_decoder_input_ids, (1, 0), value=0)
+
+ train_idxs = train_mask.nonzero(as_tuple=True)
+ backbone_last_hidden_states = backbone_hidden_states[train_idxs[0], train_idxs[1] - 1, :]
+ depth_decoder_labels = labels[train_mask]
+
+ depth_decoder_outputs = self.depth_decoder(
+ input_ids=depth_decoder_input_ids,
+ backbone_last_hidden_state=backbone_last_hidden_states,
+ use_cache=use_cache,
+ return_dict=True,
+ labels=depth_decoder_labels,
+ **kwargs,
+ )
+
+ depth_decoder_loss = depth_decoder_outputs.loss
+ loss = backbone_loss + depth_decoder_loss
+
+ return CsmOutputWithPast(
+ loss=loss,
+ backbone_loss=backbone_loss,
+ depth_decoder_loss=depth_decoder_loss,
+ logits=backbone_logits,
+ past_key_values=backbone_outputs.past_key_values,
+ hidden_states=backbone_outputs.hidden_states,
+ attentions=backbone_outputs.attentions,
+ depth_decoder_logits=depth_decoder_outputs.logits if depth_decoder_outputs is not None else None,
+ depth_decoder_past_key_values=depth_decoder_outputs.past_key_values
+ if depth_decoder_outputs is not None
+ else None,
+ depth_decoder_hidden_states=depth_decoder_outputs.hidden_states
+ if depth_decoder_outputs is not None
+ else None,
+ depth_decoder_attentions=depth_decoder_outputs.attentions if depth_decoder_outputs is not None else None,
+ )
+
+
+__all__ = [
+ "CsmPreTrainedModel",
+ "CsmBackboneModel",
+ "CsmDepthDecoderModel",
+ "CsmDepthDecoderForCausalLM",
+ "CsmForConditionalGeneration",
+]
diff --git a/third_party/transformers/src/transformers/models/csm/processing_csm.py b/third_party/transformers/src/transformers/models/csm/processing_csm.py
new file mode 100644
index 0000000000000000000000000000000000000000..f96dd26f1b5fe9ff371239b26a8876727f09c9e0
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/csm/processing_csm.py
@@ -0,0 +1,322 @@
+# Copyright 2025 Sesame and The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import math
+from pathlib import Path
+from typing import Any
+
+import numpy as np
+
+from ...utils import auto_docstring, is_soundfile_available, is_torch_available
+
+
+if is_torch_available():
+ import torch
+
+if is_soundfile_available():
+ import soundfile as sf
+
+from ...audio_utils import AudioInput, make_list_of_audio
+from ...feature_extraction_utils import BatchFeature
+from ...processing_utils import AudioKwargs, ProcessingKwargs, ProcessorMixin, Unpack
+from ...tokenization_utils_base import PreTokenizedInput, TextInput
+
+
+class CsmAudioKwargs(AudioKwargs, total=False):
+ """
+ encoded_length_kwargs (`dict[str, Any]`, *optional*):
+ Dictionary of keyword arguments used to compute the encoded audio sequence length. This includes parameters
+ such as `kernel_sizes`, `strides`, `dilations`, and `use_causal_conv` that define the convolutional layers
+ used in audio encoding. The encoded length is used to determine how many audio tokens to generate for each
+ audio input in the text sequence.
+ """
+
+ encoded_length_kwargs: dict[str, Any] | None
+
+
+class CsmProcessorKwargs(ProcessingKwargs, total=False):
+ audio_kwargs: CsmAudioKwargs
+ _defaults = {
+ "text_kwargs": {
+ "padding": True,
+ "padding_side": "left",
+ "add_special_tokens": False,
+ },
+ "audio_kwargs": {
+ "encoded_length_kwargs": {
+ "kernel_sizes": [7, 3, 1, 8, 3, 1, 10, 3, 1, 12, 3, 1, 16, 3, 4],
+ "strides": [1, 1, 1, 4, 1, 1, 5, 1, 1, 6, 1, 1, 8, 1, 2],
+ "dilations": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
+ "use_causal_conv": True,
+ },
+ "sampling_rate": 24000,
+ },
+ "common_kwargs": {"return_tensors": "pt"},
+ }
+
+
+@auto_docstring
+class CsmProcessor(ProcessorMixin):
+ def __init__(
+ self,
+ feature_extractor,
+ tokenizer,
+ chat_template=None,
+ ):
+ if not hasattr(tokenizer, "audio_token"):
+ self.audio_token = "<|AUDIO|>"
+ self.audio_token_id = tokenizer.convert_tokens_to_ids(self.audio_token)
+ else:
+ self.audio_token = tokenizer.audio_token
+ self.audio_token_id = tokenizer.audio_token_id
+
+ if not hasattr(tokenizer, "audio_eos_token"):
+ self.audio_eos_token = "<|audio_eos|>"
+ self.audio_eos_token_id = tokenizer.convert_tokens_to_ids(self.audio_eos_token)
+ else:
+ self.audio_eos_token = tokenizer.audio_eos_token
+ self.audio_eos_token_id = tokenizer.audio_eos_token_id
+
+ super().__init__(feature_extractor, tokenizer, chat_template=chat_template)
+
+ @staticmethod
+ def _get_encoded_length(audio_length, kernel_sizes=None, strides=None, dilations=None, use_causal_conv=None):
+ """
+ Compute the length of the encoded audio sequence.
+
+ Args:
+ audio_length (int): The length of the audio sequence.
+ kernel_sizes (list[int]): The kernel sizes for the convolutional layers.
+ strides (list[int]): The strides for the convolutional layers.
+ use_causal_conv (bool): Whether to use causal convolutions.
+ """
+ cur_length = audio_length
+
+ if kernel_sizes is None or strides is None or dilations is None or use_causal_conv is None:
+ return cur_length
+
+ for kernel_size, stride, dilation in zip(kernel_sizes, strides, dilations):
+ effective_kernel_size = (kernel_size - 1) * dilation + 1
+ padding_total = kernel_size - stride
+ padding_right = padding_total // 2
+ padding_left = padding_total - padding_right
+
+ n_frames = (cur_length - effective_kernel_size + padding_total) / stride + 1
+ n_frames = math.ceil(n_frames) - 1
+ ideal_length = n_frames * stride + kernel_size - padding_total
+ extra_padding = ideal_length - cur_length
+
+ if use_causal_conv:
+ padding_left = padding_total
+ padding_right = extra_padding
+ else:
+ padding_right = padding_right + extra_padding
+
+ cur_length = cur_length + padding_left + padding_right
+ cur_length = (cur_length - dilation * (kernel_size - 1) - 1) // stride + 1
+
+ return cur_length
+
+ def save_audio(
+ self,
+ audio: AudioInput,
+ saving_path: str | Path | list[str | Path],
+ **kwargs: Unpack[CsmProcessorKwargs],
+ ):
+ # TODO: @eustlb, this should be in AudioProcessor
+ if not is_soundfile_available():
+ raise ImportError("Please install `soundfile` to save audio files.")
+
+ # ensure correct audio input
+ audio = make_list_of_audio(audio)
+
+ # ensure correct saving path
+ if isinstance(saving_path, (str, Path)):
+ saving_path = [saving_path]
+ elif not (isinstance(saving_path, (list, tuple)) and all(isinstance(p, (str, Path)) for p in saving_path)):
+ raise ValueError("Invalid input path. Please provide a string, or a list of strings")
+
+ if len(audio) != len(saving_path):
+ raise ValueError("The number of audio and saving paths must be the same")
+
+ output_kwargs = self._merge_kwargs(
+ CsmProcessorKwargs,
+ **kwargs,
+ )
+ audio_kwargs = output_kwargs["audio_kwargs"]
+ sampling_rate = audio_kwargs["sampling_rate"]
+
+ for audio_value, p in zip(audio, saving_path):
+ if isinstance(audio_value, torch.Tensor):
+ audio_value = audio_value.cpu().float().numpy()
+ sf.write(p, audio_value, sampling_rate)
+
+ @auto_docstring
+ def __call__(
+ self,
+ text: TextInput | PreTokenizedInput | list[TextInput] | list[PreTokenizedInput] | None,
+ audio: AudioInput | None = None,
+ output_labels: bool | None = False,
+ depth_decoder_labels_ratio: float | None = 1.0,
+ **kwargs: Unpack[CsmProcessorKwargs],
+ ):
+ r"""
+ output_labels (bool, *optional*, default=False):
+ Whether to return labels for training. Indices will be in `[config.audio_token_id, -100, -101]`.
+ - `config.audio_token_id` indicates an audio frame (considering sequence length elements as frames)
+ - `-100` will be ignored in the loss computation
+ - `-101` indicates the audio frame will be used only for the backbone model (using the first codebook token as labels)
+ depth_decoder_labels_ratio (float, *optional*, default=1.0):
+ The ratio of audio frames to keep for the depth decoder labels.
+
+ Returns:
+ [`BatchFeature`]: A [`BatchFeature`] with the following fields:
+
+ - **input_ids** -- List of token ids to be fed to a model. Returned when `text` is not `None`.
+ - **input_values** -- List of audio values to be fed to a model. Returned when `audio` is not `None`.
+ - **attention_mask** -- List of indices specifying which tokens should be attended to by the model (when
+ `return_attention_mask=True` or if *"attention_mask"* is in `self.model_input_names` and if `text` is not
+ `None`).
+ - **labels** -- List of labels for the audio frames. Returned when `output_labels=True`.
+ """
+
+ output_kwargs = self._merge_kwargs(
+ CsmProcessorKwargs,
+ tokenizer_init_kwargs=self.tokenizer.init_kwargs,
+ **kwargs,
+ )
+
+ text_kwargs = output_kwargs["text_kwargs"]
+ audio_kwargs = output_kwargs["audio_kwargs"]
+ return_tensors = text_kwargs.get("return_tensors", None)
+ if return_tensors != "pt":
+ raise ValueError(f"{self.__class__.__name__} only supports `return_tensors='pt'`.")
+
+ if isinstance(text, str):
+ text = [text]
+ elif not (isinstance(text, (list, tuple)) and all(isinstance(t, str) for t in text)):
+ raise ValueError("Invalid input text. Please provide a string, or a list of strings")
+ n_audio_in_text = [t.count(self.audio_token) for t in text]
+
+ n_audio = 0
+ if audio is not None:
+ audio = make_list_of_audio(audio)
+ n_audio = len(audio)
+
+ if sum(n_audio_in_text) > 0 and n_audio != sum(n_audio_in_text):
+ if audio is None:
+ raise ValueError("No audio were provided, but there are audio tokens in the prompt")
+ else:
+ raise ValueError(
+ f"The number of audio tokens in each text ({n_audio_in_text}) should be the same as the "
+ f"number of provided audios ({n_audio})."
+ )
+
+ if audio is not None:
+ encoded_length_kwargs = audio_kwargs.pop("encoded_length_kwargs", {})
+ num_audio_tokens_list = [
+ self._get_encoded_length(audio_array.shape[-1], **encoded_length_kwargs) for audio_array in audio
+ ]
+ num_audio_tokens_list_copy = num_audio_tokens_list.copy()
+
+ # expand the text to repeat the audio token for the corresponding number of frames
+ expanded_text = []
+ for sample in text:
+ replace_str = []
+ while self.audio_token in sample:
+ num_audio_tokens = num_audio_tokens_list_copy.pop(0)
+ expanded_audio_token = self.audio_token * num_audio_tokens
+
+ replace_str.append(expanded_audio_token)
+ sample = sample.replace(self.audio_token, "", 1)
+
+ while "" in sample:
+ sample = sample.replace("", replace_str.pop(0), 1)
+ expanded_text.append(sample)
+
+ text = expanded_text
+
+ encoding = self.tokenizer(text, **text_kwargs)
+ data = {}
+ data.update(encoding)
+
+ if audio is not None:
+ audio_kwargs.pop("return_attention_mask", None) # not supported by the feature extractor
+
+ concatenated_audio, input_values_cutoffs = [], []
+ offset = 0
+ for n_audio in n_audio_in_text:
+ if n_audio == 0:
+ concatenated_audio.append(np.zeros(0))
+ input_values_cutoffs.append(torch.tensor([-1]))
+ else:
+ concatenated_audio.append(
+ np.concatenate(
+ [
+ el.cpu().numpy() if isinstance(el, torch.Tensor) else el
+ for el in audio[offset : offset + n_audio]
+ ],
+ axis=-1,
+ )
+ )
+ input_values_cutoffs.append(
+ torch.tensor([el.shape[-1] for el in audio[offset : offset + n_audio]]).cumsum(dim=-1)
+ )
+ offset += n_audio
+
+ audio_inputs = self.feature_extractor(concatenated_audio, **audio_kwargs)
+ audio_inputs.pop("padding_mask", None) # not applicable here
+ data.update(audio_inputs)
+
+ # pad and stack the audio cut idxs
+ max_len = max(cut_idxs.shape[-1] for cut_idxs in input_values_cutoffs)
+ input_values_cutoffs = [
+ torch.nn.functional.pad(cut_idxs, (0, max_len - cut_idxs.shape[-1]), value=-1)
+ for cut_idxs in input_values_cutoffs
+ ]
+ data["input_values_cutoffs"] = torch.stack(input_values_cutoffs, dim=0)
+
+ if output_labels:
+ audio_frame_idxs = (data["input_ids"] == self.audio_token_id).nonzero()
+ n_audio_frames = audio_frame_idxs.shape[0]
+
+ if depth_decoder_labels_ratio <= 1.0:
+ rand_idxs = torch.randperm(n_audio_frames)[: int(n_audio_frames * (1 - depth_decoder_labels_ratio))]
+ skip_frames_idxs = audio_frame_idxs[rand_idxs]
+ else:
+ skip_frames_idxs = audio_frame_idxs
+
+ labels = torch.where(
+ (data["input_ids"] == self.audio_token_id) | (data["input_ids"] == self.audio_eos_token_id),
+ data["input_ids"],
+ -100,
+ )
+ labels[skip_frames_idxs[:, 0], skip_frames_idxs[:, 1]] = -101
+
+ data["labels"] = labels
+
+ return BatchFeature(data=data, tensor_type=return_tensors)
+
+ @property
+ def model_input_names(self):
+ tokenizer_input_names = self.tokenizer.model_input_names
+ feature_extractor_input_names = self.feature_extractor.model_input_names
+
+ # Remove `padding_mask`, it is popped and not used when processing. Make a copy of list when removing
+ # otherwise `self.feature_extractor.model_input_names` is also modified
+ feature_extractor_input_names = [name for name in feature_extractor_input_names if name != "padding_mask"]
+ return list(tokenizer_input_names + feature_extractor_input_names + ["input_values_cutoffs"])
+
+
+__all__ = ["CsmProcessor"]
diff --git a/third_party/transformers/src/transformers/models/cwm/__init__.py b/third_party/transformers/src/transformers/models/cwm/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..61d8efb9a3b257fa4a7d28f1798adc43c99dadc9
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/cwm/__init__.py
@@ -0,0 +1,28 @@
+# Copyright 2025 the HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_cwm import *
+ from .modeling_cwm import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/cwm/configuration_cwm.py b/third_party/transformers/src/transformers/models/cwm/configuration_cwm.py
new file mode 100644
index 0000000000000000000000000000000000000000..ecc3743da19de42e220ffad171cf5bc0abf48027
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/cwm/configuration_cwm.py
@@ -0,0 +1,125 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/cwm/modular_cwm.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_cwm.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2025
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...utils import auto_docstring
+
+
+@auto_docstring(checkpoint="facebook/cwm")
+@strict
+class CwmConfig(PreTrainedConfig):
+ r"""
+ ```python
+ >>> from transformers import CwmModel, CwmConfig
+
+ >>> # Initializing a Cwm cwm-7b style configuration
+ >>> configuration = CwmConfig()
+
+ >>> # Initializing a model from the cwm-7b style configuration
+ >>> model = CwmModel(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "cwm"
+ keys_to_ignore_at_inference = ["past_key_values"]
+ # Default tensor parallel plan for base model `CwmModel`
+ base_model_tp_plan = {
+ "layers.*.self_attn.q_proj": "colwise",
+ "layers.*.self_attn.k_proj": "colwise",
+ "layers.*.self_attn.v_proj": "colwise",
+ "layers.*.self_attn.o_proj": "rowwise",
+ "layers.*.mlp.gate_proj": "colwise",
+ "layers.*.mlp.up_proj": "colwise",
+ "layers.*.mlp.down_proj": "rowwise",
+ }
+ base_model_pp_plan = {
+ "embed_tokens": (["input_ids"], ["inputs_embeds"]),
+ "layers": (["hidden_states", "attention_mask"], ["hidden_states"]),
+ "norm": (["hidden_states"], ["hidden_states"]),
+ }
+
+ vocab_size: int = 128256
+ hidden_size: int = 6144
+ intermediate_size: int = 21504
+ num_hidden_layers: int = 64
+ num_attention_heads: int = 48
+ num_key_value_heads: int = 8
+ hidden_act: str = "silu"
+ max_position_embeddings: int = 131072
+ initializer_range: float = 0.02
+ rms_norm_eps: float = 1e-5
+ use_cache: bool = True
+ pad_token_id: int | None = None
+ bos_token_id: int = 128000
+ eos_token_id: int | list[int] | None = None
+ pretraining_tp: int = 1
+ tie_word_embeddings: bool = False
+ rope_parameters: dict | None = None
+ attention_dropout: float | int = 0.0
+ mlp_bias: bool = False
+ head_dim: int = 128
+ default_theta = 1_000_000.0
+ sliding_window: int = 8192
+ layer_types: list[str] | None = None # ["full_attention"|"sliding_attention"] per layer
+
+ def __post_init__(self, **kwargs):
+ if self.rope_parameters is None:
+ self.rope_parameters = {
+ "rope_theta": 1_000_000.0,
+ "factor": 16.0,
+ "high_freq_factor": 4.0,
+ "low_freq_factor": 1.0,
+ "original_max_position_embeddings": 8192,
+ "rope_type": "llama3",
+ }
+
+ if self.layer_types is None:
+ # Default pattern: every 4th layer uses full attention, others use sliding attention
+ window_pattern = 4
+ self.layer_types = [
+ ("full_attention" if (i % window_pattern == 0) else "sliding_attention")
+ for i in range(self.num_hidden_layers)
+ ]
+
+ self.sliding_window = int(self.sliding_window) if self.sliding_window else None
+ self.layer_types = list(self.layer_types)
+ self.eos_token_id = self.eos_token_id if self.eos_token_id is not None else [128001, 128008, 128009]
+ if self.head_dim is None:
+ self.head_dim = self.hidden_size // self.num_attention_heads
+ if self.num_key_value_heads is None:
+ self.num_key_value_heads = self.num_attention_heads
+
+ super().__post_init__(**kwargs)
+
+ def validate_architecture(self):
+ """Part of `@strict`-powered validation. Validates the architecture of the config."""
+ if self.hidden_size % self.num_attention_heads != 0:
+ raise ValueError(
+ f"The hidden size ({self.hidden_size}) is not a multiple of the number of attention "
+ f"heads ({self.num_attention_heads})."
+ )
+
+
+__all__ = ["CwmConfig"]
diff --git a/third_party/transformers/src/transformers/models/cwm/modeling_cwm.py b/third_party/transformers/src/transformers/models/cwm/modeling_cwm.py
new file mode 100644
index 0000000000000000000000000000000000000000..3e0eb0504be0df9b0e6a705acdfd2d1f1e079991
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/cwm/modeling_cwm.py
@@ -0,0 +1,500 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/cwm/modular_cwm.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_cwm.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2025
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+from collections.abc import Callable
+from typing import Optional
+
+import torch
+from torch import nn
+
+from ...activations import ACT2FN
+from ...cache_utils import Cache, DynamicCache
+from ...generation import GenerationMixin
+from ...integrations import use_kernel_forward_from_hub, use_kernel_func_from_hub, use_kernelized_func
+from ...masking_utils import create_causal_mask, create_sliding_window_causal_mask
+from ...modeling_flash_attention_utils import FlashAttentionKwargs
+from ...modeling_layers import GradientCheckpointingLayer
+from ...modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast
+from ...modeling_rope_utils import ROPE_INIT_FUNCTIONS, dynamic_rope_update
+from ...modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
+from ...processing_utils import Unpack
+from ...utils import TransformersKwargs, auto_docstring, can_return_tuple
+from ...utils.generic import maybe_autocast, merge_with_config_defaults
+from ...utils.output_capturing import capture_outputs
+from .configuration_cwm import CwmConfig
+
+
+class CwmRotaryEmbedding(nn.Module):
+ inv_freq: torch.Tensor # fix linting for `register_buffer`
+
+ def __init__(self, config: CwmConfig, device=None):
+ super().__init__()
+ self.max_seq_len_cached = config.max_position_embeddings
+ self.original_max_seq_len = config.max_position_embeddings
+
+ self.config = config
+
+ self.rope_type = self.config.rope_parameters["rope_type"]
+ rope_init_fn: Callable = self.compute_default_rope_parameters
+ if self.rope_type != "default":
+ rope_init_fn = ROPE_INIT_FUNCTIONS[self.rope_type]
+ inv_freq, self.attention_scaling = rope_init_fn(self.config, device)
+
+ self.register_buffer("inv_freq", inv_freq, persistent=False)
+ self.register_buffer("original_inv_freq", inv_freq.clone(), persistent=False)
+
+ @staticmethod
+ def compute_default_rope_parameters(
+ config: CwmConfig | None = None,
+ device: Optional["torch.device"] = None,
+ seq_len: int | None = None,
+ ) -> tuple["torch.Tensor", float]:
+ """
+ Computes the inverse frequencies according to the original RoPE implementation
+ Args:
+ config ([`~transformers.PreTrainedConfig`]):
+ The model configuration.
+ device (`torch.device`):
+ The device to use for initialization of the inverse frequencies.
+ seq_len (`int`, *optional*):
+ The current sequence length. Unused for this type of RoPE.
+ Returns:
+ Tuple of (`torch.Tensor`, `float`), containing the inverse frequencies for the RoPE embeddings and the
+ post-processing scaling factor applied to the computed cos/sin (unused in this type of RoPE).
+ """
+ base = config.rope_parameters["rope_theta"]
+ dim = getattr(config, "head_dim", None) or config.hidden_size // config.num_attention_heads
+
+ attention_factor = 1.0 # Unused in this type of RoPE
+
+ # Compute the inverse frequencies
+ inv_freq = 1.0 / (
+ base ** (torch.arange(0, dim, 2, dtype=torch.int64).to(device=device, dtype=torch.float) / dim)
+ )
+ return inv_freq, attention_factor
+
+ @torch.no_grad()
+ @dynamic_rope_update # power user: used with advanced RoPE types (e.g. dynamic rope)
+ def forward(self, x, position_ids):
+ inv_freq_expanded = self.inv_freq[None, :, None].float().expand(position_ids.shape[0], -1, 1).to(x.device)
+ position_ids_expanded = position_ids[:, None, :].float()
+
+ device_type = x.device.type if isinstance(x.device.type, str) and x.device.type != "mps" else "cpu"
+ with maybe_autocast(device_type=device_type, enabled=False): # Force float32
+ freqs = (inv_freq_expanded.float() @ position_ids_expanded.float()).transpose(1, 2)
+ emb = torch.cat((freqs, freqs), dim=-1)
+ cos = emb.cos() * self.attention_scaling
+ sin = emb.sin() * self.attention_scaling
+
+ return cos.to(dtype=x.dtype), sin.to(dtype=x.dtype)
+
+
+def rotate_half(x):
+ """Rotates half the hidden dims of the input."""
+ x1 = x[..., : x.shape[-1] // 2]
+ x2 = x[..., x.shape[-1] // 2 :]
+ return torch.cat((-x2, x1), dim=-1)
+
+
+@use_kernel_func_from_hub("rotary_pos_emb")
+def apply_rotary_pos_emb(q, k, cos, sin, unsqueeze_dim=1):
+ """Applies Rotary Position Embedding to the query and key tensors.
+
+ Args:
+ q (`torch.Tensor`): The query tensor.
+ k (`torch.Tensor`): The key tensor.
+ cos (`torch.Tensor`): The cosine part of the rotary embedding.
+ sin (`torch.Tensor`): The sine part of the rotary embedding.
+ unsqueeze_dim (`int`, *optional*, defaults to 1):
+ The 'unsqueeze_dim' argument specifies the dimension along which to unsqueeze cos[position_ids] and
+ sin[position_ids] so that they can be properly broadcasted to the dimensions of q and k. For example, note
+ that cos[position_ids] and sin[position_ids] have the shape [batch_size, seq_len, head_dim]. Then, if q and
+ k have the shape [batch_size, heads, seq_len, head_dim], then setting unsqueeze_dim=1 makes
+ cos[position_ids] and sin[position_ids] broadcastable to the shapes of q and k. Similarly, if q and k have
+ the shape [batch_size, seq_len, heads, head_dim], then set unsqueeze_dim=2.
+ Returns:
+ `tuple(torch.Tensor)` comprising of the query and key tensors rotated using the Rotary Position Embedding.
+ """
+ cos = cos.unsqueeze(unsqueeze_dim)
+ sin = sin.unsqueeze(unsqueeze_dim)
+ q_embed = (q * cos) + (rotate_half(q) * sin)
+ k_embed = (k * cos) + (rotate_half(k) * sin)
+ return q_embed, k_embed
+
+
+def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
+ """
+ This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
+ num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
+ """
+ batch, num_key_value_heads, slen, head_dim = hidden_states.shape
+ if n_rep == 1:
+ return hidden_states
+ hidden_states = hidden_states[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim)
+ return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
+
+
+def eager_attention_forward(
+ module: nn.Module,
+ query: torch.Tensor,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ attention_mask: torch.Tensor | None,
+ scaling: float,
+ dropout: float = 0.0,
+ **kwargs: Unpack[TransformersKwargs],
+):
+ key_states = repeat_kv(key, module.num_key_value_groups)
+ value_states = repeat_kv(value, module.num_key_value_groups)
+
+ attn_weights = torch.matmul(query, key_states.transpose(2, 3)) * scaling
+ if attention_mask is not None:
+ attn_weights = attn_weights + attention_mask
+
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query.dtype)
+ attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
+ attn_output = torch.matmul(attn_weights, value_states)
+ attn_output = attn_output.transpose(1, 2).contiguous()
+
+ return attn_output, attn_weights
+
+
+@use_kernelized_func(apply_rotary_pos_emb)
+class CwmAttention(nn.Module):
+ """Multi-headed attention from 'Attention Is All You Need' paper"""
+
+ def __init__(self, config: CwmConfig, layer_idx: int):
+ super().__init__()
+ self.layer_type = config.layer_types[layer_idx] if hasattr(config, "layer_types") else None
+ self.config = config
+ self.layer_idx = layer_idx
+ self.head_dim = getattr(config, "head_dim", config.hidden_size // config.num_attention_heads)
+ self.num_key_value_groups = config.num_attention_heads // config.num_key_value_heads
+ self.scaling = self.head_dim**-0.5
+ self.attention_dropout = config.attention_dropout
+ self.is_causal = True
+ self.q_proj = torch.nn.Linear(config.hidden_size, config.num_attention_heads * self.head_dim, bias=False)
+ self.k_proj = torch.nn.Linear(config.hidden_size, config.num_key_value_heads * self.head_dim, bias=False)
+ self.v_proj = torch.nn.Linear(config.hidden_size, config.num_key_value_heads * self.head_dim, bias=False)
+ self.o_proj = nn.Linear(config.num_attention_heads * self.head_dim, config.hidden_size, bias=False)
+ self.sliding_window = config.sliding_window if self.layer_type == "sliding_attention" else None
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor],
+ attention_mask: torch.Tensor | None,
+ past_key_values: Cache | None = None,
+ **kwargs: Unpack[FlashAttentionKwargs],
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
+ input_shape = hidden_states.shape[:-1]
+ hidden_shape = (*input_shape, -1, self.head_dim)
+
+ query_states = self.q_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+ key_states = self.k_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+ value_states = self.v_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+
+ cos, sin = position_embeddings
+ query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin)
+
+ if past_key_values is not None:
+ key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx)
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ query_states,
+ key_states,
+ value_states,
+ attention_mask,
+ dropout=0.0 if not self.training else self.attention_dropout,
+ scaling=self.scaling,
+ sliding_window=self.sliding_window, # main diff with Llama
+ **kwargs,
+ )
+
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
+ attn_output = self.o_proj(attn_output)
+ return attn_output, attn_weights
+
+
+@use_kernel_forward_from_hub("RMSNorm")
+class CwmRMSNorm(nn.Module):
+ def __init__(self, hidden_size, eps: float = 1e-6) -> None:
+ """
+ CwmRMSNorm is equivalent to T5LayerNorm
+ """
+ super().__init__()
+ self.weight = nn.Parameter(torch.ones(hidden_size))
+ self.variance_epsilon = eps
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ input_dtype = hidden_states.dtype
+ hidden_states = hidden_states.to(torch.float32)
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
+ hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
+ return self.weight * hidden_states.to(input_dtype)
+
+ def extra_repr(self):
+ return f"{tuple(self.weight.shape)}, eps={self.variance_epsilon}"
+
+
+class CwmMLP(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+ self.config = config
+ self.hidden_size = config.hidden_size
+ self.intermediate_size = config.intermediate_size
+ self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=config.mlp_bias)
+ self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=config.mlp_bias)
+ self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=config.mlp_bias)
+ self.act_fn = ACT2FN[config.hidden_act]
+
+ def forward(self, x):
+ down_proj = self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
+ return down_proj
+
+
+class CwmDecoderLayer(GradientCheckpointingLayer):
+ def __init__(self, config: CwmConfig, layer_idx: int):
+ super().__init__()
+ self.hidden_size = config.hidden_size
+ self.self_attn = CwmAttention(config=config, layer_idx=layer_idx)
+
+ self.mlp = CwmMLP(config)
+ self.input_layernorm = CwmRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.post_attention_layernorm = CwmRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ use_cache: bool | None = False,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> torch.Tensor:
+ residual = hidden_states
+ hidden_states = self.input_layernorm(hidden_states)
+ # Self Attention
+ hidden_states, _ = self.self_attn(
+ hidden_states=hidden_states,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ position_embeddings=position_embeddings,
+ **kwargs,
+ )
+ hidden_states = residual + hidden_states
+
+ # Fully Connected
+ residual = hidden_states
+ hidden_states = self.post_attention_layernorm(hidden_states)
+ hidden_states = self.mlp(hidden_states)
+ hidden_states = residual + hidden_states
+ return hidden_states
+
+
+@auto_docstring
+class CwmPreTrainedModel(PreTrainedModel):
+ config: CwmConfig
+ base_model_prefix = "model"
+ supports_gradient_checkpointing = True
+ _no_split_modules = ["CwmDecoderLayer"]
+ _skip_keys_device_placement = ["past_key_values"]
+ _supports_flash_attn = True
+ _supports_sdpa = True
+ _supports_flex_attn = True
+
+ _can_compile_fullgraph = True
+ _supports_attention_backend = True
+ _can_record_outputs = {
+ "hidden_states": CwmDecoderLayer,
+ "attentions": CwmAttention,
+ }
+
+
+class CwmModelOutputWithPast(BaseModelOutputWithPast):
+ pass
+
+
+@auto_docstring
+class CwmModel(CwmPreTrainedModel):
+ config_class = CwmConfig
+
+ def __init__(self, config: CwmConfig):
+ super().__init__(config)
+ self.padding_idx = config.pad_token_id
+ self.vocab_size = config.vocab_size
+
+ self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)
+ self.layers = torch.nn.ModuleList(
+ [CwmDecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
+ )
+ self.norm = CwmRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.rotary_emb = CwmRotaryEmbedding(config=config)
+ self.gradient_checkpointing = False
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @merge_with_config_defaults
+ @capture_outputs
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ use_cache: bool | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> CwmModelOutputWithPast:
+ 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 None:
+ inputs_embeds: torch.Tensor = self.embed_tokens(input_ids)
+
+ if use_cache and past_key_values is None:
+ past_key_values = DynamicCache(config=self.config)
+
+ if position_ids is None:
+ past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
+ position_ids = torch.arange(inputs_embeds.shape[1], device=inputs_embeds.device) + past_seen_tokens
+ position_ids = position_ids.unsqueeze(0)
+
+ if not isinstance(causal_mask_mapping := attention_mask, dict):
+ mask_kwargs = {
+ "config": self.config,
+ "inputs_embeds": inputs_embeds,
+ "attention_mask": attention_mask,
+ "past_key_values": past_key_values,
+ "position_ids": position_ids,
+ }
+ sliding_mask_kwargs = mask_kwargs.copy()
+
+ causal_mask_mapping = {
+ "full_attention": create_causal_mask(**mask_kwargs),
+ "sliding_attention": create_sliding_window_causal_mask(**sliding_mask_kwargs),
+ }
+
+ hidden_states = inputs_embeds
+ position_embeddings = self.rotary_emb(hidden_states, position_ids)
+
+ for i, decoder_layer in enumerate(self.layers[: self.config.num_hidden_layers]):
+ hidden_states = decoder_layer(
+ hidden_states,
+ attention_mask=causal_mask_mapping[self.config.layer_types[i]],
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ position_embeddings=position_embeddings,
+ **kwargs,
+ )
+
+ hidden_states = self.norm(hidden_states)
+ return CwmModelOutputWithPast(
+ last_hidden_state=hidden_states,
+ past_key_values=past_key_values,
+ )
+
+
+@auto_docstring
+class CwmForCausalLM(CwmPreTrainedModel, GenerationMixin):
+ _tied_weights_keys = {"lm_head.weight": "model.embed_tokens.weight"}
+ _tp_plan = {"lm_head": "colwise_gather_output"}
+ _pp_plan = {"lm_head": (["hidden_states"], ["logits"])}
+
+ def __init__(self, config):
+ super().__init__(config)
+ self.model = CwmModel(config)
+ self.vocab_size = config.vocab_size
+ self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> CausalLMOutputWithPast:
+ r"""
+ Example:
+
+ ```python
+ >>> from transformers import AutoTokenizer, CwmForCausalLM
+
+ >>> model = CwmForCausalLM.from_pretrained("meta-cwm/Cwm-2-7b-hf")
+ >>> tokenizer = AutoTokenizer.from_pretrained("meta-cwm/Cwm-2-7b-hf")
+
+ >>> prompt = "Hey, are you conscious? Can you talk to me?"
+ >>> inputs = tokenizer(prompt, return_tensors="pt")
+
+ >>> # Generate
+ >>> generate_ids = model.generate(inputs.input_ids, max_length=30)
+ >>> tokenizer.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]
+ "Hey, are you conscious? Can you talk to me?\nI'm not conscious, but I can talk to you."
+ ```"""
+ outputs: BaseModelOutputWithPast = self.model(
+ input_ids=input_ids,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ hidden_states = outputs.last_hidden_state
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(logits=logits, labels=labels, vocab_size=self.config.vocab_size, **kwargs)
+
+ return CausalLMOutputWithPast(
+ loss=loss,
+ logits=logits,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ )
+
+
+__all__ = ["CwmPreTrainedModel", "CwmModel", "CwmForCausalLM"]
diff --git a/third_party/transformers/src/transformers/models/cwm/modular_cwm.py b/third_party/transformers/src/transformers/models/cwm/modular_cwm.py
new file mode 100644
index 0000000000000000000000000000000000000000..c9571a9b91ea14718d8522942d2cec0c8ebc1449
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/cwm/modular_cwm.py
@@ -0,0 +1,196 @@
+# Copyright 2025
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import torch
+from huggingface_hub.dataclasses import strict
+
+from ...cache_utils import Cache, DynamicCache
+from ...masking_utils import create_causal_mask, create_sliding_window_causal_mask
+from ...modeling_outputs import BaseModelOutputWithPast
+from ...processing_utils import Unpack
+from ...utils import TransformersKwargs, auto_docstring, logging
+from ..llama.configuration_llama import LlamaConfig
+from ..llama.modeling_llama import (
+ LlamaDecoderLayer,
+ LlamaForCausalLM,
+ LlamaModel,
+ LlamaPreTrainedModel,
+)
+from ..qwen2.modeling_qwen2 import Qwen2Attention, Qwen2RotaryEmbedding
+
+
+logger = logging.get_logger(__name__)
+
+
+@auto_docstring(checkpoint="facebook/cwm")
+@strict
+class CwmConfig(LlamaConfig):
+ model_type = "cwm"
+ default_theta = 1_000_000.0
+
+ vocab_size: int = 128256
+ hidden_size: int = 6144
+ intermediate_size: int = 21504
+ num_hidden_layers: int = 64
+ num_attention_heads: int = 48
+ num_key_value_heads: int = 8
+ head_dim: int = 128
+ hidden_act: str = "silu"
+ max_position_embeddings: int = 131072
+ initializer_range: float = 0.02
+ rms_norm_eps: float = 1e-5
+ use_cache: bool = True
+ pad_token_id: int | None = None
+ eos_token_id: int | list[int] | None = None
+ bos_token_id: int = 128000
+ tie_word_embeddings: bool = False
+ attention_dropout: float | int = 0.0
+ pretraining_tp: int = 1
+ mlp_bias: bool = False
+ rope_parameters: dict | None = None
+ sliding_window: int = 8192
+ layer_types: list[str] | None = None # ["full_attention"|"sliding_attention"] per layer
+
+ attention_bias = AttributeError()
+
+ def __post_init__(self, **kwargs):
+ if self.rope_parameters is None:
+ self.rope_parameters = {
+ "rope_theta": 1_000_000.0,
+ "factor": 16.0,
+ "high_freq_factor": 4.0,
+ "low_freq_factor": 1.0,
+ "original_max_position_embeddings": 8192,
+ "rope_type": "llama3",
+ }
+
+ if self.layer_types is None:
+ # Default pattern: every 4th layer uses full attention, others use sliding attention
+ window_pattern = 4
+ self.layer_types = [
+ ("full_attention" if (i % window_pattern == 0) else "sliding_attention")
+ for i in range(self.num_hidden_layers)
+ ]
+
+ self.sliding_window = int(self.sliding_window) if self.sliding_window else None
+ self.layer_types = list(self.layer_types)
+ self.eos_token_id = self.eos_token_id if self.eos_token_id is not None else [128001, 128008, 128009]
+ super().__post_init__(**kwargs)
+
+
+class CwmRotaryEmbedding(Qwen2RotaryEmbedding):
+ pass
+
+
+class CwmAttention(Qwen2Attention):
+ def __init__(self, config: CwmConfig, layer_idx: int):
+ super().__init__(config=config, layer_idx=layer_idx)
+ self.q_proj = torch.nn.Linear(config.hidden_size, config.num_attention_heads * self.head_dim, bias=False)
+ self.k_proj = torch.nn.Linear(config.hidden_size, config.num_key_value_heads * self.head_dim, bias=False)
+ self.v_proj = torch.nn.Linear(config.hidden_size, config.num_key_value_heads * self.head_dim, bias=False)
+
+
+class CwmDecoderLayer(LlamaDecoderLayer):
+ def __init__(self, config: CwmConfig, layer_idx: int):
+ super().__init__(config=config, layer_idx=layer_idx)
+ self.self_attn = CwmAttention(config=config, layer_idx=layer_idx)
+
+
+class CwmPreTrainedModel(LlamaPreTrainedModel):
+ pass
+
+
+class CwmModelOutputWithPast(BaseModelOutputWithPast):
+ pass
+
+
+class CwmModel(LlamaModel):
+ config_class = CwmConfig
+
+ def __init__(self, config: CwmConfig):
+ super().__init__(config)
+ self.layers = torch.nn.ModuleList(
+ [CwmDecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
+ )
+
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ use_cache: bool | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> CwmModelOutputWithPast:
+ 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 None:
+ inputs_embeds: torch.Tensor = self.embed_tokens(input_ids)
+
+ if use_cache and past_key_values is None:
+ past_key_values = DynamicCache(config=self.config)
+
+ if position_ids is None:
+ past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
+ position_ids = torch.arange(inputs_embeds.shape[1], device=inputs_embeds.device) + past_seen_tokens
+ position_ids = position_ids.unsqueeze(0)
+
+ if not isinstance(causal_mask_mapping := attention_mask, dict):
+ mask_kwargs = {
+ "config": self.config,
+ "inputs_embeds": inputs_embeds,
+ "attention_mask": attention_mask,
+ "past_key_values": past_key_values,
+ "position_ids": position_ids,
+ }
+ sliding_mask_kwargs = mask_kwargs.copy()
+
+ causal_mask_mapping = {
+ "full_attention": create_causal_mask(**mask_kwargs),
+ "sliding_attention": create_sliding_window_causal_mask(**sliding_mask_kwargs),
+ }
+
+ hidden_states = inputs_embeds
+ position_embeddings = self.rotary_emb(hidden_states, position_ids)
+
+ for i, decoder_layer in enumerate(self.layers[: self.config.num_hidden_layers]):
+ hidden_states = decoder_layer(
+ hidden_states,
+ attention_mask=causal_mask_mapping[self.config.layer_types[i]],
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ position_embeddings=position_embeddings,
+ **kwargs,
+ )
+
+ hidden_states = self.norm(hidden_states)
+ return CwmModelOutputWithPast(
+ last_hidden_state=hidden_states,
+ past_key_values=past_key_values,
+ )
+
+
+class CwmForCausalLM(LlamaForCausalLM):
+ pass
+
+
+__all__ = [
+ "CwmConfig",
+ "CwmPreTrainedModel",
+ "CwmModel",
+ "CwmForCausalLM",
+]
diff --git a/third_party/transformers/src/transformers/models/deepseek_v2/__init__.py b/third_party/transformers/src/transformers/models/deepseek_v2/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..c9aaf5f0fccd2d155613c244c4f5391201712e44
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/deepseek_v2/__init__.py
@@ -0,0 +1,29 @@
+# Copyright 2025 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_deepseek_v2 import *
+ from .modeling_deepseek_v2 import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/deepseek_v2/configuration_deepseek_v2.py b/third_party/transformers/src/transformers/models/deepseek_v2/configuration_deepseek_v2.py
new file mode 100644
index 0000000000000000000000000000000000000000..1b8005f8efefba4ff13c2aac79cc19c50bf30918
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/deepseek_v2/configuration_deepseek_v2.py
@@ -0,0 +1,131 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/deepseek_v2/modular_deepseek_v2.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_deepseek_v2.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2025 HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...modeling_rope_utils import RopeParameters
+from ...utils import auto_docstring
+
+
+@auto_docstring(checkpoint="deepseek-ai/DeepSeek-V2-Lite")
+@strict
+class DeepseekV2Config(PreTrainedConfig):
+ r"""
+ first_k_dense_replace (`int`, *optional*, defaults to 0):
+ Number of dense layers in the shallow layers before switching to MoE layers.
+ n_group (`int`, *optional*):
+ Number of groups for routed experts.
+ topk_method (`str`, *optional*, defaults to `"greedy"`):
+ The method used for selecting top-k experts in the routed gate mechanism.
+
+ Example:
+
+ ```python
+ >>> from transformers import DeepseekV2Model, DeepseekV2Config
+ >>> # Initializing a DeepSeek-V2 style configuration
+ >>> configuration = DeepseekV2Config()
+ >>> # Accessing the model configuration
+ >>> model = DeepseekV2Model(configuration)
+ >>> print(model.config)
+ ```
+ """
+
+ model_type = "deepseek_v2"
+ keys_to_ignore_at_inference = ["past_key_values"]
+
+ base_model_tp_plan = {
+ "layers.*.self_attn.q_proj": "colwise",
+ "layers.*.self_attn.q_b_proj": "colwise",
+ "layers.*.self_attn.kv_a_proj_with_mqa": "mla_kv_a_proj",
+ "layers.*.self_attn.kv_b_proj": "colwise",
+ "layers.*.self_attn.o_proj": "rowwise",
+ "layers.*.mlp.experts.gate_up_proj": "packed_colwise",
+ "layers.*.mlp.experts.down_proj": "rowwise",
+ "layers.*.mlp.experts": "moe_tp_experts",
+ "layers.*.mlp.shared_experts.gate_proj": "colwise",
+ "layers.*.mlp.shared_experts.up_proj": "colwise",
+ "layers.*.mlp.shared_experts.down_proj": "rowwise",
+ "layers.*.mlp.gate_proj": "colwise",
+ "layers.*.mlp.up_proj": "colwise",
+ "layers.*.mlp.down_proj": "rowwise",
+ }
+ base_model_pp_plan = {
+ "embed_tokens": (["input_ids"], ["inputs_embeds"]),
+ "layers": (["hidden_states", "attention_mask"], ["hidden_states"]),
+ "norm": (["hidden_states"], ["hidden_states"]),
+ }
+
+ vocab_size: int = 32000
+ hidden_size: int = 4096
+ intermediate_size: int = 11008
+ num_hidden_layers: int = 32
+ num_attention_heads: int = 32
+ num_key_value_heads: int | None = None
+ hidden_act: str = "silu"
+ max_position_embeddings: int = 2048
+ initializer_range: float = 0.02
+ rms_norm_eps: float = 1e-6
+ use_cache: bool = True
+ pad_token_id: int | None = None
+ bos_token_id: int | None = 1
+ eos_token_id: int | list[int] | None = 2
+ pretraining_tp: int | None = 1
+ tie_word_embeddings: bool = False
+ rope_parameters: RopeParameters | dict | None = None
+ attention_bias: bool = False
+ attention_dropout: float | None = 0.0
+ mlp_bias: bool = False
+ head_dim: int | None = None
+ first_k_dense_replace: int = 0
+ kv_lora_rank: int = 512
+ q_lora_rank: int | None = 1536
+ n_group: int | None = None
+ n_routed_experts: int = 64
+ n_shared_experts: int = 2
+ qk_nope_head_dim: int = 128
+ qk_rope_head_dim: int = 64
+ routed_scaling_factor: float = 1.0
+ topk_group: int | None = None
+ topk_method: str | None = "greedy"
+ norm_topk_prob: bool | None = False
+ v_head_dim: int = 128
+ num_experts_per_tok: int | None = None
+ moe_intermediate_size: int = 1407
+
+ def __post_init__(self, **kwargs):
+ self.head_dim = self.qk_rope_head_dim
+ if self.head_dim is None:
+ self.head_dim = self.hidden_size // self.num_attention_heads
+ if self.num_key_value_heads is None:
+ self.num_key_value_heads = self.num_attention_heads
+
+ super().__post_init__(**kwargs)
+
+ def validate_architecture(self):
+ """Part of `@strict`-powered validation. Validates the architecture of the config."""
+ if self.hidden_size % self.num_attention_heads != 0:
+ raise ValueError(
+ f"The hidden size ({self.hidden_size}) is not a multiple of the number of attention "
+ f"heads ({self.num_attention_heads})."
+ )
+
+
+__all__ = ["DeepseekV2Config"]
diff --git a/third_party/transformers/src/transformers/models/deepseek_v2/modeling_deepseek_v2.py b/third_party/transformers/src/transformers/models/deepseek_v2/modeling_deepseek_v2.py
new file mode 100644
index 0000000000000000000000000000000000000000..3ef8266218f77ff78cdc234ebf94bce3c9e1c26b
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/deepseek_v2/modeling_deepseek_v2.py
@@ -0,0 +1,624 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/deepseek_v2/modular_deepseek_v2.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_deepseek_v2.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2025 HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from collections.abc import Callable
+from typing import Optional
+
+import torch
+import torch.nn.functional as F
+from torch import nn
+
+from ... import initialization as init
+from ...activations import ACT2FN
+from ...cache_utils import Cache, DynamicCache
+from ...generation import GenerationMixin
+from ...integrations import use_experts_implementation, use_kernel_forward_from_hub
+from ...masking_utils import create_causal_mask
+from ...modeling_layers import GenericForSequenceClassification, GradientCheckpointingLayer
+from ...modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast
+from ...modeling_rope_utils import ROPE_INIT_FUNCTIONS, dynamic_rope_update
+from ...modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
+from ...processing_utils import Unpack
+from ...utils import TransformersKwargs, auto_docstring, can_return_tuple
+from ...utils.generic import is_flash_attention_requested, maybe_autocast, merge_with_config_defaults
+from ...utils.output_capturing import capture_outputs
+from .configuration_deepseek_v2 import DeepseekV2Config
+
+
+@use_experts_implementation
+class DeepseekV2Experts(nn.Module):
+ """Collection of expert weights stored as 3D tensors."""
+
+ def __init__(self, config):
+ super().__init__()
+ self.num_experts = config.n_routed_experts
+ self.hidden_dim = config.hidden_size
+ self.intermediate_dim = config.moe_intermediate_size
+ self.gate_up_proj = nn.Parameter(torch.empty(self.num_experts, 2 * self.intermediate_dim, self.hidden_dim))
+ self.down_proj = nn.Parameter(torch.empty(self.num_experts, self.hidden_dim, self.intermediate_dim))
+ self.act_fn = ACT2FN[config.hidden_act]
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ top_k_index: torch.Tensor,
+ top_k_weights: torch.Tensor,
+ ) -> torch.Tensor:
+ final_hidden_states = torch.zeros_like(hidden_states)
+ with torch.no_grad():
+ expert_mask = torch.nn.functional.one_hot(top_k_index, num_classes=self.num_experts)
+ expert_mask = expert_mask.permute(2, 1, 0)
+ expert_hit = torch.greater(expert_mask.sum(dim=(-1, -2)), 0).nonzero()
+
+ for expert_idx in expert_hit:
+ expert_idx = expert_idx[0]
+ if expert_idx == self.num_experts:
+ continue
+ top_k_pos, token_idx = torch.where(expert_mask[expert_idx])
+ current_state = hidden_states[token_idx]
+ gate, up = nn.functional.linear(current_state, self.gate_up_proj[expert_idx]).chunk(2, dim=-1)
+ current_hidden_states = self.act_fn(gate) * up
+ current_hidden_states = nn.functional.linear(current_hidden_states, self.down_proj[expert_idx])
+ current_hidden_states = current_hidden_states * top_k_weights[token_idx, top_k_pos, None]
+ final_hidden_states.index_add_(0, token_idx, current_hidden_states.to(final_hidden_states.dtype))
+
+ return final_hidden_states
+
+
+class DeepseekV2Moe(nn.Module):
+ def __init__(self, config: DeepseekV2Config):
+ super().__init__()
+ self.config = config
+ self.experts = DeepseekV2Experts(config)
+ self.gate = nn.Linear(config.hidden_size, config.n_routed_experts, bias=False)
+ if config.n_shared_experts is not None:
+ intermediate_size = config.moe_intermediate_size * config.n_shared_experts
+ self.shared_experts = DeepseekV2MLP(config=config, intermediate_size=intermediate_size)
+ self.routed_scaling_factor = config.routed_scaling_factor
+ self.topk_method = config.topk_method
+ self.num_group = config.n_group
+ self.top_k = config.num_experts_per_tok
+ self.topk_group = config.topk_group
+
+ def route_tokens_to_experts(self, router_logits):
+ batch_size, seq_len, hidden_dim = router_logits.shape
+ router_logits = router_logits.view(-1, hidden_dim)
+ router_logits = router_logits.softmax(dim=-1, dtype=torch.float32)
+ if self.topk_method == "greedy":
+ topk_weight, topk_idx = torch.topk(router_logits, k=self.top_k, dim=-1, sorted=False)
+ elif self.topk_method == "group_limited_greedy":
+ group_scores = router_logits.view(batch_size * seq_len, self.num_group, -1).max(dim=-1).values
+ group_idx = torch.topk(group_scores, k=self.topk_group, dim=-1, sorted=False)[1]
+ group_mask = torch.zeros_like(group_scores)
+ group_mask.scatter_(1, group_idx, 1)
+ score_mask = (
+ group_mask.unsqueeze(-1)
+ .expand(batch_size * seq_len, self.num_group, self.num_experts // self.num_group)
+ .reshape(batch_size * seq_len, -1)
+ )
+ tmp_scores = router_logits.masked_fill(~score_mask.bool(), 0.0)
+ topk_weight, topk_idx = torch.topk(tmp_scores, k=self.top_k, dim=-1, sorted=False)
+
+ topk_weight = topk_weight * self.routed_scaling_factor
+ return topk_idx, topk_weight
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ residuals = hidden_states
+ orig_shape = hidden_states.shape
+ router_logits = nn.functional.linear(hidden_states.type(torch.float32), self.gate.weight.type(torch.float32))
+ topk_indices, topk_weights = self.route_tokens_to_experts(router_logits)
+ hidden_states = hidden_states.view(-1, hidden_states.shape[-1])
+ hidden_states = self.experts(hidden_states, topk_indices, topk_weights).view(*orig_shape)
+ hidden_states = hidden_states + self.shared_experts(residuals)
+ return hidden_states
+
+
+class DeepseekV2MLP(nn.Module):
+ def __init__(self, config: DeepseekV2Config, hidden_size=None, intermediate_size=None):
+ super().__init__()
+ self.config = config
+ self.hidden_size = config.hidden_size if hidden_size is None else hidden_size
+ self.intermediate_size = config.intermediate_size if intermediate_size is None else intermediate_size
+ self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=config.mlp_bias)
+ self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=config.mlp_bias)
+ self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=config.mlp_bias)
+ self.act_fn = ACT2FN[config.hidden_act]
+
+ def forward(self, x):
+ down_proj = self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
+ return down_proj
+
+
+@use_kernel_forward_from_hub("RMSNorm")
+class DeepseekV2RMSNorm(nn.Module):
+ def __init__(self, hidden_size, eps: float = 1e-6) -> None:
+ """
+ DeepseekV2RMSNorm is equivalent to T5LayerNorm
+ """
+ super().__init__()
+ self.weight = nn.Parameter(torch.ones(hidden_size))
+ self.variance_epsilon = eps
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ input_dtype = hidden_states.dtype
+ hidden_states = hidden_states.to(torch.float32)
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
+ hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
+ return self.weight * hidden_states.to(input_dtype)
+
+ def extra_repr(self):
+ return f"{tuple(self.weight.shape)}, eps={self.variance_epsilon}"
+
+
+class DeepseekV2RotaryEmbedding(nn.Module):
+ inv_freq: torch.Tensor # fix linting for `register_buffer`
+
+ def __init__(self, config: DeepseekV2Config, device=None):
+ super().__init__()
+ self.max_seq_len_cached = config.max_position_embeddings
+ self.original_max_seq_len = config.max_position_embeddings
+
+ self.config = config
+
+ self.rope_type = self.config.rope_parameters["rope_type"]
+ rope_init_fn: Callable = self.compute_default_rope_parameters
+ if self.rope_type != "default":
+ rope_init_fn = ROPE_INIT_FUNCTIONS[self.rope_type]
+ inv_freq, self.attention_scaling = rope_init_fn(self.config, device)
+
+ self.register_buffer("inv_freq", inv_freq, persistent=False)
+ self.register_buffer("original_inv_freq", inv_freq.clone(), persistent=False)
+
+ @staticmethod
+ def compute_default_rope_parameters(
+ config: DeepseekV2Config | None = None,
+ device: Optional["torch.device"] = None,
+ seq_len: int | None = None,
+ ) -> tuple["torch.Tensor", float]:
+ """
+ Computes the inverse frequencies according to the original RoPE implementation
+ Args:
+ config ([`~transformers.PreTrainedConfig`]):
+ The model configuration.
+ device (`torch.device`):
+ The device to use for initialization of the inverse frequencies.
+ seq_len (`int`, *optional*):
+ The current sequence length. Unused for this type of RoPE.
+ Returns:
+ Tuple of (`torch.Tensor`, `float`), containing the inverse frequencies for the RoPE embeddings and the
+ post-processing scaling factor applied to the computed cos/sin (unused in this type of RoPE).
+ """
+ base = config.rope_parameters["rope_theta"]
+ dim = getattr(config, "head_dim", None) or config.hidden_size // config.num_attention_heads
+
+ attention_factor = 1.0 # Unused in this type of RoPE
+
+ # Compute the inverse frequencies
+ inv_freq = 1.0 / (
+ base ** (torch.arange(0, dim, 2, dtype=torch.int64).to(device=device, dtype=torch.float) / dim)
+ )
+ return inv_freq, attention_factor
+
+ @torch.no_grad()
+ @dynamic_rope_update # power user: used with advanced RoPE types (e.g. dynamic rope)
+ def forward(self, x, position_ids):
+ inv_freq_expanded = self.inv_freq[None, :, None].float().expand(position_ids.shape[0], -1, 1)
+ position_ids_expanded = position_ids[:, None, :].float()
+
+ device_type = x.device.type if isinstance(x.device.type, str) and x.device.type != "mps" else "cpu"
+ with maybe_autocast(device_type=device_type, enabled=False): # Force float32
+ freqs = (inv_freq_expanded.to(x.device) @ position_ids_expanded).transpose(1, 2)
+ freqs_cis = torch.polar(torch.ones_like(freqs), freqs) # Convert to complex representation
+ freqs_cis = freqs_cis * self.attention_scaling
+
+ return freqs_cis
+
+
+def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
+ """
+ This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
+ num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
+ """
+ batch, num_key_value_heads, slen, head_dim = hidden_states.shape
+ if n_rep == 1:
+ return hidden_states
+ hidden_states = hidden_states[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim)
+ return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
+
+
+def eager_attention_forward(
+ module: nn.Module,
+ query: torch.Tensor,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ attention_mask: torch.Tensor | None,
+ scaling: float,
+ dropout: float = 0.0,
+ **kwargs: Unpack[TransformersKwargs],
+):
+ key_states = repeat_kv(key, module.num_key_value_groups)
+ value_states = repeat_kv(value, module.num_key_value_groups)
+
+ attn_weights = torch.matmul(query, key_states.transpose(2, 3)) * scaling
+ if attention_mask is not None:
+ attn_weights = attn_weights + attention_mask
+
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query.dtype)
+ attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
+ attn_output = torch.matmul(attn_weights, value_states)
+ attn_output = attn_output.transpose(1, 2).contiguous()
+
+ return attn_output, attn_weights
+
+
+def apply_rotary_emb(
+ xq: torch.Tensor,
+ xk: torch.Tensor,
+ freqs_cis: torch.Tensor,
+) -> tuple[torch.Tensor, torch.Tensor]:
+ xq_ = torch.view_as_complex(xq.float().reshape(*xq.shape[:-1], -1, 2))
+ xk_ = torch.view_as_complex(xk.float().reshape(*xk.shape[:-1], -1, 2))
+
+ # Broadcast to [1, 1, seq_len, dim // 2]
+ freqs_cis = freqs_cis.unsqueeze(1).to(xq_.device)
+
+ xq_out = torch.view_as_real(xq_ * freqs_cis).flatten(3).type_as(xq)
+ xk_out = torch.view_as_real(xk_ * freqs_cis).flatten(3).type_as(xk)
+ return xq_out, xk_out
+
+
+class DeepseekV2Attention(nn.Module):
+ """Multi-headed attention from 'Attention Is All You Need' paper"""
+
+ def __init__(self, config: DeepseekV2Config, layer_idx: int | None = None):
+ super().__init__()
+ self.config = config
+ self.layer_idx = layer_idx
+ self.attention_dropout = config.attention_dropout
+ self.hidden_size = config.hidden_size
+ self.num_heads = config.num_attention_heads
+ self.head_dim = config.head_dim
+ self.max_position_embeddings = config.max_position_embeddings
+
+ self.q_lora_rank = config.q_lora_rank
+ self.qk_rope_head_dim = config.qk_rope_head_dim
+ self.kv_lora_rank = config.kv_lora_rank
+ self.v_head_dim = config.v_head_dim
+ self.qk_nope_head_dim = config.qk_nope_head_dim
+ self.qk_head_dim = config.qk_nope_head_dim + config.qk_rope_head_dim
+ self.num_key_value_groups = config.num_attention_heads // config.num_key_value_heads
+
+ self.is_causal = True
+
+ if self.q_lora_rank is None:
+ self.q_proj = nn.Linear(self.hidden_size, self.num_heads * self.qk_head_dim, bias=False)
+ else:
+ self.q_a_proj = nn.Linear(self.hidden_size, config.q_lora_rank, bias=config.attention_bias)
+ self.q_a_layernorm = DeepseekV2RMSNorm(config.q_lora_rank)
+ self.q_b_proj = nn.Linear(config.q_lora_rank, self.num_heads * self.qk_head_dim, bias=False)
+
+ self.kv_a_proj_with_mqa = nn.Linear(
+ self.hidden_size,
+ config.kv_lora_rank + config.qk_rope_head_dim,
+ bias=config.attention_bias,
+ )
+ self.kv_a_layernorm = DeepseekV2RMSNorm(config.kv_lora_rank)
+ self.kv_b_proj = nn.Linear(
+ config.kv_lora_rank,
+ self.num_heads * (self.qk_head_dim - self.qk_rope_head_dim + self.v_head_dim),
+ bias=False,
+ )
+
+ self.o_proj = nn.Linear(
+ self.num_heads * self.v_head_dim,
+ self.hidden_size,
+ bias=config.attention_bias,
+ )
+
+ self.scaling = self.qk_head_dim ** (-0.5)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ attention_mask: torch.Tensor | None = None,
+ past_key_values: Cache | None = None,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
+ **kwargs,
+ ) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
+ batch_size, seq_length = hidden_states.shape[:-1]
+ query_shape = (batch_size, seq_length, -1, self.qk_head_dim)
+ key_shape = (batch_size, seq_length, -1, self.qk_nope_head_dim + self.v_head_dim)
+
+ if self.q_lora_rank is None:
+ q = self.q_proj(hidden_states)
+ else:
+ q = self.q_b_proj(self.q_a_layernorm(self.q_a_proj(hidden_states)))
+ q = q.view(query_shape).transpose(1, 2)
+ q_nope, q_pe = torch.split(q, [self.qk_nope_head_dim, self.qk_rope_head_dim], dim=-1)
+
+ compressed_kv = self.kv_a_proj_with_mqa(hidden_states)
+ k_nope, k_pe = torch.split(compressed_kv, [self.kv_lora_rank, self.qk_rope_head_dim], dim=-1)
+ k_nope = self.kv_b_proj(self.kv_a_layernorm(k_nope)).view(key_shape).transpose(1, 2)
+ k_nope, value_states = torch.split(k_nope, [self.qk_nope_head_dim, self.v_head_dim], dim=-1)
+
+ k_pe = k_pe.view(batch_size, 1, seq_length, self.qk_rope_head_dim)
+
+ q_pe, k_pe = apply_rotary_emb(q_pe, k_pe, position_embeddings.to(q_pe.device))
+
+ k_pe = k_pe.expand(*k_nope.shape[:-1], -1)
+ query_states = torch.cat((q_nope, q_pe), dim=-1)
+ key_states = torch.cat((k_nope, k_pe), dim=-1)
+
+ if past_key_values is not None:
+ key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx)
+
+ if is_flash_attention_requested(self.config) and self.qk_head_dim != self.v_head_dim:
+ value_states = F.pad(value_states, [0, self.qk_head_dim - self.v_head_dim])
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ query_states,
+ key_states,
+ value_states,
+ attention_mask,
+ dropout=0.0 if not self.training else self.attention_dropout,
+ scaling=self.scaling,
+ **kwargs,
+ )
+
+ if is_flash_attention_requested(self.config) and self.qk_head_dim != self.v_head_dim:
+ attn_output = attn_output[:, :, :, : self.v_head_dim]
+
+ attn_output = attn_output.reshape(batch_size, seq_length, -1).contiguous()
+ attn_output = self.o_proj(attn_output)
+ return attn_output, attn_weights
+
+
+class DeepseekV2DecoderLayer(GradientCheckpointingLayer):
+ def __init__(self, config: DeepseekV2Config, layer_idx: int):
+ super().__init__()
+ self.hidden_size = config.hidden_size
+
+ self.self_attn = DeepseekV2Attention(config=config, layer_idx=layer_idx)
+ self.mlp = DeepseekV2Moe(config) if layer_idx >= config.first_k_dense_replace else DeepseekV2MLP(config)
+
+ self.input_layernorm = DeepseekV2RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.post_attention_layernorm = DeepseekV2RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ use_cache: bool | None = False,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> torch.Tensor:
+ residual = hidden_states
+ hidden_states = self.input_layernorm(hidden_states)
+ # Self Attention
+ hidden_states, _ = self.self_attn(
+ hidden_states=hidden_states,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ position_embeddings=position_embeddings,
+ **kwargs,
+ )
+ hidden_states = residual + hidden_states
+
+ # Fully Connected
+ residual = hidden_states
+ hidden_states = self.post_attention_layernorm(hidden_states)
+ hidden_states = self.mlp(hidden_states)
+ hidden_states = residual + hidden_states
+ return hidden_states
+
+
+@auto_docstring
+class DeepseekV2PreTrainedModel(PreTrainedModel):
+ config: DeepseekV2Config
+ base_model_prefix = "model"
+ supports_gradient_checkpointing = True
+ _no_split_modules = ["DeepseekV2DecoderLayer"]
+ _skip_keys_device_placement = ["past_key_values"]
+ _supports_flash_attn = True
+ _supports_sdpa = True
+ _supports_flex_attn = True
+
+ _can_compile_fullgraph = True
+ _supports_attention_backend = True
+ _can_record_outputs = {
+ "hidden_states": DeepseekV2DecoderLayer,
+ "attentions": DeepseekV2Attention,
+ }
+
+ @torch.no_grad()
+ def _init_weights(self, module):
+ super()._init_weights(module)
+ if isinstance(module, DeepseekV2Experts):
+ init.normal_(module.gate_up_proj, mean=0.0, std=self.config.initializer_range)
+ init.normal_(module.down_proj, mean=0.0, std=self.config.initializer_range)
+
+
+@auto_docstring
+class DeepseekV2Model(DeepseekV2PreTrainedModel):
+ def __init__(self, config: DeepseekV2Config):
+ super().__init__(config)
+ self.padding_idx = config.pad_token_id
+ self.vocab_size = config.vocab_size
+
+ self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)
+ self.layers = nn.ModuleList(
+ [DeepseekV2DecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
+ )
+ self.norm = DeepseekV2RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.rotary_emb = DeepseekV2RotaryEmbedding(config=config)
+ self.gradient_checkpointing = False
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @merge_with_config_defaults
+ @capture_outputs
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ use_cache: bool | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> BaseModelOutputWithPast:
+ 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 None:
+ inputs_embeds: torch.Tensor = self.embed_tokens(input_ids)
+
+ if use_cache and past_key_values is None:
+ past_key_values = DynamicCache(config=self.config)
+
+ if position_ids is None:
+ past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
+ position_ids = torch.arange(inputs_embeds.shape[1], device=inputs_embeds.device) + past_seen_tokens
+ position_ids = position_ids.unsqueeze(0)
+
+ causal_mask = create_causal_mask(
+ config=self.config,
+ inputs_embeds=inputs_embeds,
+ attention_mask=attention_mask,
+ past_key_values=past_key_values,
+ position_ids=position_ids,
+ )
+
+ hidden_states = inputs_embeds
+ position_embeddings = self.rotary_emb(hidden_states, position_ids=position_ids)
+
+ for decoder_layer in self.layers[: self.config.num_hidden_layers]:
+ hidden_states = decoder_layer(
+ hidden_states,
+ attention_mask=causal_mask,
+ position_embeddings=position_embeddings,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ hidden_states = self.norm(hidden_states)
+ return BaseModelOutputWithPast(
+ last_hidden_state=hidden_states,
+ past_key_values=past_key_values,
+ )
+
+
+@auto_docstring
+class DeepseekV2ForCausalLM(DeepseekV2PreTrainedModel, GenerationMixin):
+ _tied_weights_keys = {"lm_head.weight": "model.embed_tokens.weight"}
+ _tp_plan = {"lm_head": "colwise_gather_output"}
+ _pp_plan = {"lm_head": (["hidden_states"], ["logits"])}
+
+ def __init__(self, config):
+ super().__init__(config)
+ self.model = DeepseekV2Model(config)
+ self.vocab_size = config.vocab_size
+ self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> CausalLMOutputWithPast:
+ r"""
+ Example:
+
+ ```python
+ >>> from transformers import AutoTokenizer, DeepseekV2ForCausalLM
+
+ >>> model = DeepseekV2ForCausalLM.from_pretrained("meta-deepseek_v2/DeepseekV2-2-7b-hf")
+ >>> tokenizer = AutoTokenizer.from_pretrained("meta-deepseek_v2/DeepseekV2-2-7b-hf")
+
+ >>> prompt = "Hey, are you conscious? Can you talk to me?"
+ >>> inputs = tokenizer(prompt, return_tensors="pt")
+
+ >>> # Generate
+ >>> generate_ids = model.generate(inputs.input_ids, max_length=30)
+ >>> tokenizer.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]
+ "Hey, are you conscious? Can you talk to me?\nI'm not conscious, but I can talk to you."
+ ```"""
+ outputs: BaseModelOutputWithPast = self.model(
+ input_ids=input_ids,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ hidden_states = outputs.last_hidden_state
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(logits=logits, labels=labels, vocab_size=self.config.vocab_size, **kwargs)
+
+ return CausalLMOutputWithPast(
+ loss=loss,
+ logits=logits,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ )
+
+
+class DeepseekV2ForSequenceClassification(GenericForSequenceClassification, DeepseekV2PreTrainedModel):
+ pass
+
+
+__all__ = [
+ "DeepseekV2PreTrainedModel",
+ "DeepseekV2Model",
+ "DeepseekV2ForCausalLM",
+ "DeepseekV2ForSequenceClassification",
+]
diff --git a/third_party/transformers/src/transformers/models/deepseek_v2/modular_deepseek_v2.py b/third_party/transformers/src/transformers/models/deepseek_v2/modular_deepseek_v2.py
new file mode 100644
index 0000000000000000000000000000000000000000..5644c7dc2990ea8d2b08173c6bb6d1043955b782
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/deepseek_v2/modular_deepseek_v2.py
@@ -0,0 +1,375 @@
+# Copyright 2025 HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from collections.abc import Callable
+
+import torch
+import torch.nn.functional as F
+from huggingface_hub.dataclasses import strict
+from torch import nn
+
+from ... import initialization as init
+from ...cache_utils import Cache
+from ...modeling_rope_utils import RopeParameters, dynamic_rope_update
+from ...modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
+from ...utils import auto_docstring, logging
+from ...utils.generic import is_flash_attention_requested, maybe_autocast
+from ..llama.configuration_llama import LlamaConfig
+from ..llama.modeling_llama import (
+ LlamaDecoderLayer,
+ LlamaForCausalLM,
+ LlamaForSequenceClassification,
+ LlamaMLP,
+ LlamaModel,
+ LlamaPreTrainedModel,
+ LlamaRMSNorm,
+ LlamaRotaryEmbedding,
+ eager_attention_forward,
+)
+from ..qwen2_moe.modeling_qwen2_moe import Qwen2MoeExperts
+
+
+logger = logging.get_logger(__name__)
+
+
+@auto_docstring(checkpoint="deepseek-ai/DeepSeek-V2-Lite")
+@strict
+class DeepseekV2Config(LlamaConfig):
+ r"""
+ first_k_dense_replace (`int`, *optional*, defaults to 0):
+ Number of dense layers in the shallow layers before switching to MoE layers.
+ n_group (`int`, *optional*):
+ Number of groups for routed experts.
+ topk_method (`str`, *optional*, defaults to `"greedy"`):
+ The method used for selecting top-k experts in the routed gate mechanism.
+
+ Example:
+
+ ```python
+ >>> from transformers import DeepseekV2Model, DeepseekV2Config
+ >>> # Initializing a DeepSeek-V2 style configuration
+ >>> configuration = DeepseekV2Config()
+ >>> # Accessing the model configuration
+ >>> model = DeepseekV2Model(configuration)
+ >>> print(model.config)
+ ```
+ """
+
+ base_model_tp_plan = {
+ "layers.*.self_attn.q_proj": "colwise",
+ "layers.*.self_attn.q_b_proj": "colwise",
+ "layers.*.self_attn.kv_a_proj_with_mqa": "mla_kv_a_proj",
+ "layers.*.self_attn.kv_b_proj": "colwise",
+ "layers.*.self_attn.o_proj": "rowwise",
+ "layers.*.mlp.experts.gate_up_proj": "packed_colwise",
+ "layers.*.mlp.experts.down_proj": "rowwise",
+ "layers.*.mlp.experts": "moe_tp_experts",
+ "layers.*.mlp.shared_experts.gate_proj": "colwise",
+ "layers.*.mlp.shared_experts.up_proj": "colwise",
+ "layers.*.mlp.shared_experts.down_proj": "rowwise",
+ "layers.*.mlp.gate_proj": "colwise",
+ "layers.*.mlp.up_proj": "colwise",
+ "layers.*.mlp.down_proj": "rowwise",
+ }
+
+ model_type = "deepseek_v2"
+ keys_to_ignore_at_inference = ["past_key_values"]
+
+ vocab_size: int = 32000
+ hidden_size: int = 4096
+ intermediate_size: int = 11008
+ num_hidden_layers: int = 32
+ num_attention_heads: int = 32
+ num_key_value_heads: int | None = None
+ hidden_act: str = "silu"
+ max_position_embeddings: int = 2048
+ initializer_range: float = 0.02
+ rms_norm_eps: float = 1e-6
+ use_cache: bool = True
+ pad_token_id: int | None = None
+ bos_token_id: int | None = 1
+ eos_token_id: int | list[int] | None = 2
+ tie_word_embeddings: bool = False
+ rope_parameters: RopeParameters | dict | None = None
+ attention_bias: bool = False
+ attention_dropout: float | None = 0.0
+ mlp_bias: bool = False
+ first_k_dense_replace: int = 0
+ kv_lora_rank: int = 512
+ q_lora_rank: int | None = 1536
+ n_group: int | None = None
+ n_routed_experts: int = 64
+ n_shared_experts: int = 2
+ qk_nope_head_dim: int = 128
+ qk_rope_head_dim: int = 64
+ routed_scaling_factor: float = 1.0
+ topk_group: int | None = None
+ topk_method: str | None = "greedy"
+ norm_topk_prob: bool | None = False
+ v_head_dim: int = 128
+ num_experts_per_tok: int | None = None
+ moe_intermediate_size: int = 1407
+
+ def __post_init__(self, **kwargs):
+ self.head_dim = self.qk_rope_head_dim
+ super().__post_init__(**kwargs)
+
+
+def apply_rotary_emb(
+ xq: torch.Tensor,
+ xk: torch.Tensor,
+ freqs_cis: torch.Tensor,
+) -> tuple[torch.Tensor, torch.Tensor]:
+ xq_ = torch.view_as_complex(xq.float().reshape(*xq.shape[:-1], -1, 2))
+ xk_ = torch.view_as_complex(xk.float().reshape(*xk.shape[:-1], -1, 2))
+
+ # Broadcast to [1, 1, seq_len, dim // 2]
+ freqs_cis = freqs_cis.unsqueeze(1).to(xq_.device)
+
+ xq_out = torch.view_as_real(xq_ * freqs_cis).flatten(3).type_as(xq)
+ xk_out = torch.view_as_real(xk_ * freqs_cis).flatten(3).type_as(xk)
+ return xq_out, xk_out
+
+
+class DeepseekV2Experts(Qwen2MoeExperts):
+ def __init__(self, config):
+ super().__init__(config)
+ self.num_experts = config.n_routed_experts
+
+
+class DeepseekV2Moe(nn.Module):
+ def __init__(self, config: DeepseekV2Config):
+ super().__init__()
+ self.config = config
+ self.experts = DeepseekV2Experts(config)
+ self.gate = nn.Linear(config.hidden_size, config.n_routed_experts, bias=False)
+ if config.n_shared_experts is not None:
+ intermediate_size = config.moe_intermediate_size * config.n_shared_experts
+ self.shared_experts = DeepseekV2MLP(config=config, intermediate_size=intermediate_size)
+ self.routed_scaling_factor = config.routed_scaling_factor
+ self.topk_method = config.topk_method
+ self.num_group = config.n_group
+ self.top_k = config.num_experts_per_tok
+ self.topk_group = config.topk_group
+
+ def route_tokens_to_experts(self, router_logits):
+ batch_size, seq_len, hidden_dim = router_logits.shape
+ router_logits = router_logits.view(-1, hidden_dim)
+ router_logits = router_logits.softmax(dim=-1, dtype=torch.float32)
+ if self.topk_method == "greedy":
+ topk_weight, topk_idx = torch.topk(router_logits, k=self.top_k, dim=-1, sorted=False)
+ elif self.topk_method == "group_limited_greedy":
+ group_scores = router_logits.view(batch_size * seq_len, self.num_group, -1).max(dim=-1).values
+ group_idx = torch.topk(group_scores, k=self.topk_group, dim=-1, sorted=False)[1]
+ group_mask = torch.zeros_like(group_scores)
+ group_mask.scatter_(1, group_idx, 1)
+ score_mask = (
+ group_mask.unsqueeze(-1)
+ .expand(batch_size * seq_len, self.num_group, self.num_experts // self.num_group)
+ .reshape(batch_size * seq_len, -1)
+ )
+ tmp_scores = router_logits.masked_fill(~score_mask.bool(), 0.0)
+ topk_weight, topk_idx = torch.topk(tmp_scores, k=self.top_k, dim=-1, sorted=False)
+
+ topk_weight = topk_weight * self.routed_scaling_factor
+ return topk_idx, topk_weight
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ residuals = hidden_states
+ orig_shape = hidden_states.shape
+ router_logits = nn.functional.linear(hidden_states.type(torch.float32), self.gate.weight.type(torch.float32))
+ topk_indices, topk_weights = self.route_tokens_to_experts(router_logits)
+ hidden_states = hidden_states.view(-1, hidden_states.shape[-1])
+ hidden_states = self.experts(hidden_states, topk_indices, topk_weights).view(*orig_shape)
+ hidden_states = hidden_states + self.shared_experts(residuals)
+ return hidden_states
+
+
+class DeepseekV2MLP(LlamaMLP):
+ def __init__(self, config: DeepseekV2Config, hidden_size=None, intermediate_size=None):
+ super().__init__(config)
+ self.hidden_size = config.hidden_size if hidden_size is None else hidden_size
+ self.intermediate_size = config.intermediate_size if intermediate_size is None else intermediate_size
+
+
+class DeepseekV2RMSNorm(LlamaRMSNorm):
+ pass
+
+
+class DeepseekV2RotaryEmbedding(LlamaRotaryEmbedding):
+ @torch.no_grad()
+ @dynamic_rope_update # power user: used with advanced RoPE types (e.g. dynamic rope)
+ def forward(self, x, position_ids):
+ inv_freq_expanded = self.inv_freq[None, :, None].float().expand(position_ids.shape[0], -1, 1)
+ position_ids_expanded = position_ids[:, None, :].float()
+
+ device_type = x.device.type if isinstance(x.device.type, str) and x.device.type != "mps" else "cpu"
+ with maybe_autocast(device_type=device_type, enabled=False): # Force float32
+ freqs = (inv_freq_expanded.to(x.device) @ position_ids_expanded).transpose(1, 2)
+ freqs_cis = torch.polar(torch.ones_like(freqs), freqs) # Convert to complex representation
+ freqs_cis = freqs_cis * self.attention_scaling
+
+ return freqs_cis
+
+
+class DeepseekV2Attention(nn.Module):
+ """Multi-headed attention from 'Attention Is All You Need' paper"""
+
+ def __init__(self, config: DeepseekV2Config, layer_idx: int | None = None):
+ super().__init__()
+ self.config = config
+ self.layer_idx = layer_idx
+ self.attention_dropout = config.attention_dropout
+ self.hidden_size = config.hidden_size
+ self.num_heads = config.num_attention_heads
+ self.head_dim = config.head_dim
+ self.max_position_embeddings = config.max_position_embeddings
+
+ self.q_lora_rank = config.q_lora_rank
+ self.qk_rope_head_dim = config.qk_rope_head_dim
+ self.kv_lora_rank = config.kv_lora_rank
+ self.v_head_dim = config.v_head_dim
+ self.qk_nope_head_dim = config.qk_nope_head_dim
+ self.qk_head_dim = config.qk_nope_head_dim + config.qk_rope_head_dim
+ self.num_key_value_groups = config.num_attention_heads // config.num_key_value_heads
+
+ self.is_causal = True
+
+ if self.q_lora_rank is None:
+ self.q_proj = nn.Linear(self.hidden_size, self.num_heads * self.qk_head_dim, bias=False)
+ else:
+ self.q_a_proj = nn.Linear(self.hidden_size, config.q_lora_rank, bias=config.attention_bias)
+ self.q_a_layernorm = DeepseekV2RMSNorm(config.q_lora_rank)
+ self.q_b_proj = nn.Linear(config.q_lora_rank, self.num_heads * self.qk_head_dim, bias=False)
+
+ self.kv_a_proj_with_mqa = nn.Linear(
+ self.hidden_size,
+ config.kv_lora_rank + config.qk_rope_head_dim,
+ bias=config.attention_bias,
+ )
+ self.kv_a_layernorm = DeepseekV2RMSNorm(config.kv_lora_rank)
+ self.kv_b_proj = nn.Linear(
+ config.kv_lora_rank,
+ self.num_heads * (self.qk_head_dim - self.qk_rope_head_dim + self.v_head_dim),
+ bias=False,
+ )
+
+ self.o_proj = nn.Linear(
+ self.num_heads * self.v_head_dim,
+ self.hidden_size,
+ bias=config.attention_bias,
+ )
+
+ self.scaling = self.qk_head_dim ** (-0.5)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ attention_mask: torch.Tensor | None = None,
+ past_key_values: Cache | None = None,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
+ **kwargs,
+ ) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
+ batch_size, seq_length = hidden_states.shape[:-1]
+ query_shape = (batch_size, seq_length, -1, self.qk_head_dim)
+ key_shape = (batch_size, seq_length, -1, self.qk_nope_head_dim + self.v_head_dim)
+
+ if self.q_lora_rank is None:
+ q = self.q_proj(hidden_states)
+ else:
+ q = self.q_b_proj(self.q_a_layernorm(self.q_a_proj(hidden_states)))
+ q = q.view(query_shape).transpose(1, 2)
+ q_nope, q_pe = torch.split(q, [self.qk_nope_head_dim, self.qk_rope_head_dim], dim=-1)
+
+ compressed_kv = self.kv_a_proj_with_mqa(hidden_states)
+ k_nope, k_pe = torch.split(compressed_kv, [self.kv_lora_rank, self.qk_rope_head_dim], dim=-1)
+ k_nope = self.kv_b_proj(self.kv_a_layernorm(k_nope)).view(key_shape).transpose(1, 2)
+ k_nope, value_states = torch.split(k_nope, [self.qk_nope_head_dim, self.v_head_dim], dim=-1)
+
+ k_pe = k_pe.view(batch_size, 1, seq_length, self.qk_rope_head_dim)
+
+ q_pe, k_pe = apply_rotary_emb(q_pe, k_pe, position_embeddings.to(q_pe.device))
+
+ k_pe = k_pe.expand(*k_nope.shape[:-1], -1)
+ query_states = torch.cat((q_nope, q_pe), dim=-1)
+ key_states = torch.cat((k_nope, k_pe), dim=-1)
+
+ if past_key_values is not None:
+ key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx)
+
+ if is_flash_attention_requested(self.config) and self.qk_head_dim != self.v_head_dim:
+ value_states = F.pad(value_states, [0, self.qk_head_dim - self.v_head_dim])
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ query_states,
+ key_states,
+ value_states,
+ attention_mask,
+ dropout=0.0 if not self.training else self.attention_dropout,
+ scaling=self.scaling,
+ **kwargs,
+ )
+
+ if is_flash_attention_requested(self.config) and self.qk_head_dim != self.v_head_dim:
+ attn_output = attn_output[:, :, :, : self.v_head_dim]
+
+ attn_output = attn_output.reshape(batch_size, seq_length, -1).contiguous()
+ attn_output = self.o_proj(attn_output)
+ return attn_output, attn_weights
+
+
+class DeepseekV2DecoderLayer(LlamaDecoderLayer):
+ def __init__(self, config: DeepseekV2Config, layer_idx: int):
+ super().__init__(config, layer_idx)
+
+ self.self_attn = DeepseekV2Attention(config=config, layer_idx=layer_idx)
+ self.mlp = DeepseekV2Moe(config) if layer_idx >= config.first_k_dense_replace else DeepseekV2MLP(config)
+
+ self.input_layernorm = DeepseekV2RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.post_attention_layernorm = DeepseekV2RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+
+
+class DeepseekV2PreTrainedModel(LlamaPreTrainedModel):
+ @torch.no_grad()
+ def _init_weights(self, module):
+ PreTrainedModel._init_weights(self, module)
+ if isinstance(module, DeepseekV2Experts):
+ init.normal_(module.gate_up_proj, mean=0.0, std=self.config.initializer_range)
+ init.normal_(module.down_proj, mean=0.0, std=self.config.initializer_range)
+
+
+class DeepseekV2Model(LlamaModel):
+ pass
+
+
+class DeepseekV2ForCausalLM(LlamaForCausalLM):
+ pass
+
+
+class DeepseekV2ForSequenceClassification(LlamaForSequenceClassification):
+ pass
+
+
+__all__ = [
+ "DeepseekV2PreTrainedModel",
+ "DeepseekV2Model",
+ "DeepseekV2ForCausalLM",
+ "DeepseekV2ForSequenceClassification",
+ "DeepseekV2Config",
+]
diff --git a/third_party/transformers/src/transformers/models/detr/__init__.py b/third_party/transformers/src/transformers/models/detr/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..c19722d0bf3a1cbf6930d89c3e27290da4ae50e9
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/detr/__init__.py
@@ -0,0 +1,31 @@
+# Copyright 2020 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_detr import *
+ from .feature_extraction_detr import *
+ from .image_processing_detr import *
+ from .image_processing_pil_detr import *
+ from .modeling_detr import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/detr/configuration_detr.py b/third_party/transformers/src/transformers/models/detr/configuration_detr.py
new file mode 100644
index 0000000000000000000000000000000000000000..a64a6eda7fa36797517919441f8b4d57d2328d44
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/detr/configuration_detr.py
@@ -0,0 +1,114 @@
+# Copyright 2021 Facebook AI Research and The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""DETR model configuration"""
+
+from huggingface_hub.dataclasses import strict
+
+from ...backbone_utils import consolidate_backbone_kwargs_to_config
+from ...configuration_utils import PreTrainedConfig
+from ...utils import auto_docstring
+from ..auto import AutoConfig
+
+
+@auto_docstring(checkpoint="facebook/detr-resnet-50")
+@strict
+class DetrConfig(PreTrainedConfig):
+ r"""
+ num_queries (`int`, *optional*, defaults to 100):
+ Number of object queries, i.e. detection slots. This is the maximal number of objects
+ [`ConditionalDetrModel`] can detect in a single image. For COCO, we recommend 100 queries.
+ position_embedding_type (`str`, *optional*, defaults to `"sine"`):
+ Type of position embeddings to be used on top of the image features. One of `"sine"` or `"learned"`.
+ dilation (`bool`, *optional*, defaults to `False`):
+ Whether to replace stride with dilation in the last convolutional block (DC5). Only supported when
+ `use_timm_backbone` = `True`.
+
+ Examples:
+
+ ```python
+ >>> from transformers import DetrConfig, DetrModel
+
+ >>> # Initializing a DETR facebook/detr-resnet-50 style configuration
+ >>> configuration = DetrConfig()
+
+ >>> # Initializing a model (with random weights) from the facebook/detr-resnet-50 style configuration
+ >>> model = DetrModel(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "detr"
+ sub_configs = {"backbone_config": AutoConfig}
+ keys_to_ignore_at_inference = ["past_key_values"]
+ attribute_map = {
+ "hidden_size": "d_model",
+ "num_attention_heads": "encoder_attention_heads",
+ "num_hidden_layers": "encoder_layers",
+ }
+
+ backbone_config: dict | PreTrainedConfig | None = None
+ num_channels: int = 3
+ num_queries: int = 100
+ encoder_layers: int = 6
+ encoder_ffn_dim: int = 2048
+ encoder_attention_heads: int = 8
+ decoder_layers: int = 6
+ decoder_ffn_dim: int = 2048
+ decoder_attention_heads: int = 8
+ encoder_layerdrop: float | int = 0.0
+ decoder_layerdrop: float | int = 0.0
+ is_encoder_decoder: bool = True
+ activation_function: str = "relu"
+ d_model: int = 256
+ dropout: float | int = 0.1
+ attention_dropout: float | int = 0.0
+ activation_dropout: float | int = 0.0
+ init_std: float = 0.02
+ init_xavier_std: float = 1.0
+ auxiliary_loss: bool = False
+ position_embedding_type: str = "sine"
+ dilation: bool = False
+ class_cost: int = 1
+ bbox_cost: int = 5
+ giou_cost: int = 2
+ mask_loss_coefficient: int = 1
+ dice_loss_coefficient: int = 1
+ bbox_loss_coefficient: int = 5
+ giou_loss_coefficient: int = 2
+ eos_coefficient: float = 0.1
+
+ def __post_init__(self, **kwargs):
+ backbone_kwargs = kwargs.get("backbone_kwargs", {})
+ timm_default_kwargs = {
+ "num_channels": backbone_kwargs.get("num_channels", self.num_channels),
+ "features_only": True,
+ "use_pretrained_backbone": False,
+ "out_indices": backbone_kwargs.get("out_indices", [1, 2, 3, 4]),
+ }
+ if self.dilation:
+ timm_default_kwargs["output_stride"] = backbone_kwargs.get("output_stride", 16)
+
+ self.backbone_config, kwargs = consolidate_backbone_kwargs_to_config(
+ backbone_config=self.backbone_config,
+ default_backbone="resnet50",
+ default_config_type="resnet",
+ default_config_kwargs={"out_features": ["stage4"]},
+ timm_default_kwargs=timm_default_kwargs,
+ **kwargs,
+ )
+ super().__post_init__(**kwargs)
+
+
+__all__ = ["DetrConfig"]
diff --git a/third_party/transformers/src/transformers/models/detr/convert_detr_original_pytorch_checkpoint_to_pytorch.py b/third_party/transformers/src/transformers/models/detr/convert_detr_original_pytorch_checkpoint_to_pytorch.py
new file mode 100644
index 0000000000000000000000000000000000000000..91f670b33eb392b96d3f25a4780092253d1c1570
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/detr/convert_detr_original_pytorch_checkpoint_to_pytorch.py
@@ -0,0 +1,277 @@
+# Copyright 2020 The HuggingFace Inc. team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Convert DETR checkpoints with timm backbone."""
+
+import argparse
+import json
+from collections import OrderedDict
+from io import BytesIO
+from pathlib import Path
+
+import httpx
+import torch
+from huggingface_hub import hf_hub_download
+from PIL import Image
+
+from transformers import DetrConfig, DetrForObjectDetection, DetrForSegmentation, DetrImageProcessor
+from transformers.utils import logging
+
+
+logging.set_verbosity_info()
+logger = logging.get_logger(__name__)
+
+# here we list all keys to be renamed (original name on the left, our name on the right)
+rename_keys = []
+for i in range(6):
+ # encoder layers: output projection, 2 feedforward neural networks and 2 layernorms
+ rename_keys.append(
+ (f"transformer.encoder.layers.{i}.self_attn.out_proj.weight", f"encoder.layers.{i}.self_attn.out_proj.weight")
+ )
+ rename_keys.append(
+ (f"transformer.encoder.layers.{i}.self_attn.out_proj.bias", f"encoder.layers.{i}.self_attn.out_proj.bias")
+ )
+ rename_keys.append((f"transformer.encoder.layers.{i}.linear1.weight", f"encoder.layers.{i}.fc1.weight"))
+ rename_keys.append((f"transformer.encoder.layers.{i}.linear1.bias", f"encoder.layers.{i}.fc1.bias"))
+ rename_keys.append((f"transformer.encoder.layers.{i}.linear2.weight", f"encoder.layers.{i}.fc2.weight"))
+ rename_keys.append((f"transformer.encoder.layers.{i}.linear2.bias", f"encoder.layers.{i}.fc2.bias"))
+ rename_keys.append(
+ (f"transformer.encoder.layers.{i}.norm1.weight", f"encoder.layers.{i}.self_attn_layer_norm.weight")
+ )
+ rename_keys.append((f"transformer.encoder.layers.{i}.norm1.bias", f"encoder.layers.{i}.self_attn_layer_norm.bias"))
+ rename_keys.append((f"transformer.encoder.layers.{i}.norm2.weight", f"encoder.layers.{i}.final_layer_norm.weight"))
+ rename_keys.append((f"transformer.encoder.layers.{i}.norm2.bias", f"encoder.layers.{i}.final_layer_norm.bias"))
+ # decoder layers: 2 times output projection, 2 feedforward neural networks and 3 layernorms
+ rename_keys.append(
+ (f"transformer.decoder.layers.{i}.self_attn.out_proj.weight", f"decoder.layers.{i}.self_attn.out_proj.weight")
+ )
+ rename_keys.append(
+ (f"transformer.decoder.layers.{i}.self_attn.out_proj.bias", f"decoder.layers.{i}.self_attn.out_proj.bias")
+ )
+ rename_keys.append(
+ (
+ f"transformer.decoder.layers.{i}.multihead_attn.out_proj.weight",
+ f"decoder.layers.{i}.encoder_attn.out_proj.weight",
+ )
+ )
+ rename_keys.append(
+ (
+ f"transformer.decoder.layers.{i}.multihead_attn.out_proj.bias",
+ f"decoder.layers.{i}.encoder_attn.out_proj.bias",
+ )
+ )
+ rename_keys.append((f"transformer.decoder.layers.{i}.linear1.weight", f"decoder.layers.{i}.fc1.weight"))
+ rename_keys.append((f"transformer.decoder.layers.{i}.linear1.bias", f"decoder.layers.{i}.fc1.bias"))
+ rename_keys.append((f"transformer.decoder.layers.{i}.linear2.weight", f"decoder.layers.{i}.fc2.weight"))
+ rename_keys.append((f"transformer.decoder.layers.{i}.linear2.bias", f"decoder.layers.{i}.fc2.bias"))
+ rename_keys.append(
+ (f"transformer.decoder.layers.{i}.norm1.weight", f"decoder.layers.{i}.self_attn_layer_norm.weight")
+ )
+ rename_keys.append((f"transformer.decoder.layers.{i}.norm1.bias", f"decoder.layers.{i}.self_attn_layer_norm.bias"))
+ rename_keys.append(
+ (f"transformer.decoder.layers.{i}.norm2.weight", f"decoder.layers.{i}.encoder_attn_layer_norm.weight")
+ )
+ rename_keys.append(
+ (f"transformer.decoder.layers.{i}.norm2.bias", f"decoder.layers.{i}.encoder_attn_layer_norm.bias")
+ )
+ rename_keys.append((f"transformer.decoder.layers.{i}.norm3.weight", f"decoder.layers.{i}.final_layer_norm.weight"))
+ rename_keys.append((f"transformer.decoder.layers.{i}.norm3.bias", f"decoder.layers.{i}.final_layer_norm.bias"))
+
+# convolutional projection + query embeddings + layernorm of decoder + class and bounding box heads
+rename_keys.extend(
+ [
+ ("input_proj.weight", "input_projection.weight"),
+ ("input_proj.bias", "input_projection.bias"),
+ ("query_embed.weight", "query_position_embeddings.weight"),
+ ("transformer.decoder.norm.weight", "decoder.layernorm.weight"),
+ ("transformer.decoder.norm.bias", "decoder.layernorm.bias"),
+ ("class_embed.weight", "class_labels_classifier.weight"),
+ ("class_embed.bias", "class_labels_classifier.bias"),
+ ("bbox_embed.layers.0.weight", "bbox_predictor.layers.0.weight"),
+ ("bbox_embed.layers.0.bias", "bbox_predictor.layers.0.bias"),
+ ("bbox_embed.layers.1.weight", "bbox_predictor.layers.1.weight"),
+ ("bbox_embed.layers.1.bias", "bbox_predictor.layers.1.bias"),
+ ("bbox_embed.layers.2.weight", "bbox_predictor.layers.2.weight"),
+ ("bbox_embed.layers.2.bias", "bbox_predictor.layers.2.bias"),
+ ]
+)
+
+
+def rename_key(state_dict, old, new):
+ val = state_dict.pop(old)
+ state_dict[new] = val
+
+
+def rename_backbone_keys(state_dict):
+ new_state_dict = OrderedDict()
+ for key, value in state_dict.items():
+ if "backbone.0.body" in key:
+ new_key = key.replace("backbone.0.body", "backbone.conv_encoder.model")
+ new_state_dict[new_key] = value
+ else:
+ new_state_dict[key] = value
+
+ return new_state_dict
+
+
+def read_in_q_k_v(state_dict, is_panoptic=False):
+ prefix = ""
+ if is_panoptic:
+ prefix = "detr."
+
+ # first: transformer encoder
+ for i in range(6):
+ # read in weights + bias of input projection layer (in PyTorch's MultiHeadAttention, this is a single matrix + bias)
+ in_proj_weight = state_dict.pop(f"{prefix}transformer.encoder.layers.{i}.self_attn.in_proj_weight")
+ in_proj_bias = state_dict.pop(f"{prefix}transformer.encoder.layers.{i}.self_attn.in_proj_bias")
+ # next, add query, keys and values (in that order) to the state dict
+ state_dict[f"encoder.layers.{i}.self_attn.q_proj.weight"] = in_proj_weight[:256, :]
+ state_dict[f"encoder.layers.{i}.self_attn.q_proj.bias"] = in_proj_bias[:256]
+ state_dict[f"encoder.layers.{i}.self_attn.k_proj.weight"] = in_proj_weight[256:512, :]
+ state_dict[f"encoder.layers.{i}.self_attn.k_proj.bias"] = in_proj_bias[256:512]
+ state_dict[f"encoder.layers.{i}.self_attn.v_proj.weight"] = in_proj_weight[-256:, :]
+ state_dict[f"encoder.layers.{i}.self_attn.v_proj.bias"] = in_proj_bias[-256:]
+ # next: transformer decoder (which is a bit more complex because it also includes cross-attention)
+ for i in range(6):
+ # read in weights + bias of input projection layer of self-attention
+ in_proj_weight = state_dict.pop(f"{prefix}transformer.decoder.layers.{i}.self_attn.in_proj_weight")
+ in_proj_bias = state_dict.pop(f"{prefix}transformer.decoder.layers.{i}.self_attn.in_proj_bias")
+ # next, add query, keys and values (in that order) to the state dict
+ state_dict[f"decoder.layers.{i}.self_attn.q_proj.weight"] = in_proj_weight[:256, :]
+ state_dict[f"decoder.layers.{i}.self_attn.q_proj.bias"] = in_proj_bias[:256]
+ state_dict[f"decoder.layers.{i}.self_attn.k_proj.weight"] = in_proj_weight[256:512, :]
+ state_dict[f"decoder.layers.{i}.self_attn.k_proj.bias"] = in_proj_bias[256:512]
+ state_dict[f"decoder.layers.{i}.self_attn.v_proj.weight"] = in_proj_weight[-256:, :]
+ state_dict[f"decoder.layers.{i}.self_attn.v_proj.bias"] = in_proj_bias[-256:]
+ # read in weights + bias of input projection layer of cross-attention
+ in_proj_weight_cross_attn = state_dict.pop(
+ f"{prefix}transformer.decoder.layers.{i}.multihead_attn.in_proj_weight"
+ )
+ in_proj_bias_cross_attn = state_dict.pop(f"{prefix}transformer.decoder.layers.{i}.multihead_attn.in_proj_bias")
+ # next, add query, keys and values (in that order) of cross-attention to the state dict
+ state_dict[f"decoder.layers.{i}.encoder_attn.q_proj.weight"] = in_proj_weight_cross_attn[:256, :]
+ state_dict[f"decoder.layers.{i}.encoder_attn.q_proj.bias"] = in_proj_bias_cross_attn[:256]
+ state_dict[f"decoder.layers.{i}.encoder_attn.k_proj.weight"] = in_proj_weight_cross_attn[256:512, :]
+ state_dict[f"decoder.layers.{i}.encoder_attn.k_proj.bias"] = in_proj_bias_cross_attn[256:512]
+ state_dict[f"decoder.layers.{i}.encoder_attn.v_proj.weight"] = in_proj_weight_cross_attn[-256:, :]
+ state_dict[f"decoder.layers.{i}.encoder_attn.v_proj.bias"] = in_proj_bias_cross_attn[-256:]
+
+
+# We will verify our results on an image of cute cats
+def prepare_img():
+ url = "http://images.cocodataset.org/val2017/000000039769.jpg"
+ with httpx.stream("GET", url) as response:
+ image = Image.open(BytesIO(response.read()))
+ return image
+
+
+@torch.no_grad()
+def convert_detr_checkpoint(model_name, pytorch_dump_folder_path):
+ """
+ Copy/paste/tweak model's weights to our DETR structure.
+ """
+
+ # load default config
+ config = DetrConfig()
+ # set backbone and dilation attributes
+ if "resnet101" in model_name:
+ config.backbone = "resnet101"
+ if "dc5" in model_name:
+ config.dilation = True
+ is_panoptic = "panoptic" in model_name
+ if is_panoptic:
+ config.num_labels = 250
+ else:
+ config.num_labels = 91
+ repo_id = "huggingface/label-files"
+ filename = "coco-detection-id2label.json"
+ id2label = json.load(open(hf_hub_download(repo_id, filename, repo_type="dataset"), "r"))
+ id2label = {int(k): v for k, v in id2label.items()}
+ config.id2label = id2label
+ config.label2id = {v: k for k, v in id2label.items()}
+
+ # load image processor
+ format = "coco_panoptic" if is_panoptic else "coco_detection"
+ image_processor = DetrImageProcessor(format=format)
+
+ # prepare image
+ img = prepare_img()
+ encoding = image_processor(images=img, return_tensors="pt")
+ pixel_values = encoding["pixel_values"]
+
+ logger.info(f"Converting model {model_name}...")
+
+ # load original model from torch hub
+ detr = torch.hub.load("facebookresearch/detr", model_name, pretrained=True).eval()
+ state_dict = detr.state_dict()
+ # rename keys
+ for src, dest in rename_keys:
+ if is_panoptic:
+ src = "detr." + src
+ rename_key(state_dict, src, dest)
+ state_dict = rename_backbone_keys(state_dict)
+ # query, key and value matrices need special treatment
+ read_in_q_k_v(state_dict, is_panoptic=is_panoptic)
+ # important: we need to prepend a prefix to each of the base model keys as the head models use different attributes for them
+ prefix = "detr.model." if is_panoptic else "model."
+ for key in state_dict.copy():
+ if is_panoptic:
+ if (
+ key.startswith("detr")
+ and not key.startswith("class_labels_classifier")
+ and not key.startswith("bbox_predictor")
+ ):
+ val = state_dict.pop(key)
+ state_dict["detr.model" + key[4:]] = val
+ elif "class_labels_classifier" in key or "bbox_predictor" in key:
+ val = state_dict.pop(key)
+ state_dict["detr." + key] = val
+ elif key.startswith("bbox_attention") or key.startswith("mask_head"):
+ continue
+ else:
+ val = state_dict.pop(key)
+ state_dict[prefix + key] = val
+ else:
+ if not key.startswith("class_labels_classifier") and not key.startswith("bbox_predictor"):
+ val = state_dict.pop(key)
+ state_dict[prefix + key] = val
+ # finally, create HuggingFace model and load state dict
+ model = DetrForSegmentation(config) if is_panoptic else DetrForObjectDetection(config)
+ model.load_state_dict(state_dict)
+ model.eval()
+ # verify our conversion
+ original_outputs = detr(pixel_values)
+ outputs = model(pixel_values)
+ assert torch.allclose(outputs.logits, original_outputs["pred_logits"], atol=1e-4)
+ assert torch.allclose(outputs.pred_boxes, original_outputs["pred_boxes"], atol=1e-4)
+ if is_panoptic:
+ assert torch.allclose(outputs.pred_masks, original_outputs["pred_masks"], atol=1e-4)
+
+ # Save model and image processor
+ logger.info(f"Saving PyTorch model and image processor to {pytorch_dump_folder_path}...")
+ Path(pytorch_dump_folder_path).mkdir(exist_ok=True)
+ model.save_pretrained(pytorch_dump_folder_path)
+ image_processor.save_pretrained(pytorch_dump_folder_path)
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+
+ parser.add_argument(
+ "--model_name", default="detr_resnet50", type=str, help="Name of the DETR model you'd like to convert."
+ )
+ parser.add_argument(
+ "--pytorch_dump_folder_path", default=None, type=str, help="Path to the folder to output PyTorch model."
+ )
+ args = parser.parse_args()
+ convert_detr_checkpoint(args.model_name, args.pytorch_dump_folder_path)
diff --git a/third_party/transformers/src/transformers/models/detr/convert_detr_to_pytorch.py b/third_party/transformers/src/transformers/models/detr/convert_detr_to_pytorch.py
new file mode 100644
index 0000000000000000000000000000000000000000..445d77986b145787c107b6e55bbe5872a0b66b32
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/detr/convert_detr_to_pytorch.py
@@ -0,0 +1,386 @@
+# Copyright 2023 The HuggingFace Inc. team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Convert DETR checkpoints with native (Transformers) backbone."""
+
+import argparse
+import json
+from io import BytesIO
+from pathlib import Path
+
+import httpx
+import torch
+from huggingface_hub import hf_hub_download
+from PIL import Image
+
+from transformers import DetrConfig, DetrForObjectDetection, DetrForSegmentation, DetrImageProcessor, ResNetConfig
+from transformers.utils import logging
+
+
+logging.set_verbosity_info()
+logger = logging.get_logger(__name__)
+
+
+def get_detr_config(model_name):
+ # initialize config
+ if "resnet-50" in model_name:
+ backbone_config = ResNetConfig.from_pretrained("microsoft/resnet-50")
+ elif "resnet-101" in model_name:
+ backbone_config = ResNetConfig.from_pretrained("microsoft/resnet-101")
+ else:
+ raise ValueError("Model name should include either resnet50 or resnet101")
+
+ config = DetrConfig(use_timm_backbone=False, backbone_config=backbone_config)
+
+ # set label attributes
+ is_panoptic = "panoptic" in model_name
+ if is_panoptic:
+ config.num_labels = 250
+ else:
+ config.num_labels = 91
+ repo_id = "huggingface/label-files"
+ filename = "coco-detection-id2label.json"
+ id2label = json.load(open(hf_hub_download(repo_id, filename, repo_type="dataset"), "r"))
+ id2label = {int(k): v for k, v in id2label.items()}
+ config.id2label = id2label
+ config.label2id = {v: k for k, v in id2label.items()}
+
+ return config, is_panoptic
+
+
+def create_rename_keys(config):
+ # here we list all keys to be renamed (original name on the left, our name on the right)
+ rename_keys = []
+
+ # stem
+ # fmt: off
+ rename_keys.append(("backbone.0.body.conv1.weight", "backbone.conv_encoder.model.embedder.embedder.convolution.weight"))
+ rename_keys.append(("backbone.0.body.bn1.weight", "backbone.conv_encoder.model.embedder.embedder.normalization.weight"))
+ rename_keys.append(("backbone.0.body.bn1.bias", "backbone.conv_encoder.model.embedder.embedder.normalization.bias"))
+ rename_keys.append(("backbone.0.body.bn1.running_mean", "backbone.conv_encoder.model.embedder.embedder.normalization.running_mean"))
+ rename_keys.append(("backbone.0.body.bn1.running_var", "backbone.conv_encoder.model.embedder.embedder.normalization.running_var"))
+ # stages
+ for stage_idx in range(len(config.backbone_config.depths)):
+ for layer_idx in range(config.backbone_config.depths[stage_idx]):
+ # shortcut
+ if layer_idx == 0:
+ rename_keys.append(
+ (
+ f"backbone.0.body.layer{stage_idx + 1}.{layer_idx}.downsample.0.weight",
+ f"backbone.conv_encoder.model.encoder.stages.{stage_idx}.layers.{layer_idx}.shortcut.convolution.weight",
+ )
+ )
+ rename_keys.append(
+ (
+ f"backbone.0.body.layer{stage_idx + 1}.{layer_idx}.downsample.1.weight",
+ f"backbone.conv_encoder.model.encoder.stages.{stage_idx}.layers.{layer_idx}.shortcut.normalization.weight",
+ )
+ )
+ rename_keys.append(
+ (
+ f"backbone.0.body.layer{stage_idx + 1}.{layer_idx}.downsample.1.bias",
+ f"backbone.conv_encoder.model.encoder.stages.{stage_idx}.layers.{layer_idx}.shortcut.normalization.bias",
+ )
+ )
+ rename_keys.append(
+ (
+ f"backbone.0.body.layer{stage_idx + 1}.{layer_idx}.downsample.1.running_mean",
+ f"backbone.conv_encoder.model.encoder.stages.{stage_idx}.layers.{layer_idx}.shortcut.normalization.running_mean",
+ )
+ )
+ rename_keys.append(
+ (
+ f"backbone.0.body.layer{stage_idx + 1}.{layer_idx}.downsample.1.running_var",
+ f"backbone.conv_encoder.model.encoder.stages.{stage_idx}.layers.{layer_idx}.shortcut.normalization.running_var",
+ )
+ )
+ # 3 convs
+ for i in range(3):
+ rename_keys.append(
+ (
+ f"backbone.0.body.layer{stage_idx + 1}.{layer_idx}.conv{i+1}.weight",
+ f"backbone.conv_encoder.model.encoder.stages.{stage_idx}.layers.{layer_idx}.layer.{i}.convolution.weight",
+ )
+ )
+ rename_keys.append(
+ (
+ f"backbone.0.body.layer{stage_idx + 1}.{layer_idx}.bn{i+1}.weight",
+ f"backbone.conv_encoder.model.encoder.stages.{stage_idx}.layers.{layer_idx}.layer.{i}.normalization.weight",
+ )
+ )
+ rename_keys.append(
+ (
+ f"backbone.0.body.layer{stage_idx + 1}.{layer_idx}.bn{i+1}.bias",
+ f"backbone.conv_encoder.model.encoder.stages.{stage_idx}.layers.{layer_idx}.layer.{i}.normalization.bias",
+ )
+ )
+ rename_keys.append(
+ (
+ f"backbone.0.body.layer{stage_idx + 1}.{layer_idx}.bn{i+1}.running_mean",
+ f"backbone.conv_encoder.model.encoder.stages.{stage_idx}.layers.{layer_idx}.layer.{i}.normalization.running_mean",
+ )
+ )
+ rename_keys.append(
+ (
+ f"backbone.0.body.layer{stage_idx + 1}.{layer_idx}.bn{i+1}.running_var",
+ f"backbone.conv_encoder.model.encoder.stages.{stage_idx}.layers.{layer_idx}.layer.{i}.normalization.running_var",
+ )
+ )
+ # fmt: on
+
+ for i in range(config.encoder_layers):
+ # encoder layers: output projection, 2 feedforward neural networks and 2 layernorms
+ rename_keys.append(
+ (
+ f"transformer.encoder.layers.{i}.self_attn.out_proj.weight",
+ f"encoder.layers.{i}.self_attn.out_proj.weight",
+ )
+ )
+ rename_keys.append(
+ (f"transformer.encoder.layers.{i}.self_attn.out_proj.bias", f"encoder.layers.{i}.self_attn.out_proj.bias")
+ )
+ rename_keys.append((f"transformer.encoder.layers.{i}.linear1.weight", f"encoder.layers.{i}.fc1.weight"))
+ rename_keys.append((f"transformer.encoder.layers.{i}.linear1.bias", f"encoder.layers.{i}.fc1.bias"))
+ rename_keys.append((f"transformer.encoder.layers.{i}.linear2.weight", f"encoder.layers.{i}.fc2.weight"))
+ rename_keys.append((f"transformer.encoder.layers.{i}.linear2.bias", f"encoder.layers.{i}.fc2.bias"))
+ rename_keys.append(
+ (f"transformer.encoder.layers.{i}.norm1.weight", f"encoder.layers.{i}.self_attn_layer_norm.weight")
+ )
+ rename_keys.append(
+ (f"transformer.encoder.layers.{i}.norm1.bias", f"encoder.layers.{i}.self_attn_layer_norm.bias")
+ )
+ rename_keys.append(
+ (f"transformer.encoder.layers.{i}.norm2.weight", f"encoder.layers.{i}.final_layer_norm.weight")
+ )
+ rename_keys.append((f"transformer.encoder.layers.{i}.norm2.bias", f"encoder.layers.{i}.final_layer_norm.bias"))
+ # decoder layers: 2 times output projection, 2 feedforward neural networks and 3 layernorms
+ rename_keys.append(
+ (
+ f"transformer.decoder.layers.{i}.self_attn.out_proj.weight",
+ f"decoder.layers.{i}.self_attn.out_proj.weight",
+ )
+ )
+ rename_keys.append(
+ (f"transformer.decoder.layers.{i}.self_attn.out_proj.bias", f"decoder.layers.{i}.self_attn.out_proj.bias")
+ )
+ rename_keys.append(
+ (
+ f"transformer.decoder.layers.{i}.multihead_attn.out_proj.weight",
+ f"decoder.layers.{i}.encoder_attn.out_proj.weight",
+ )
+ )
+ rename_keys.append(
+ (
+ f"transformer.decoder.layers.{i}.multihead_attn.out_proj.bias",
+ f"decoder.layers.{i}.encoder_attn.out_proj.bias",
+ )
+ )
+ rename_keys.append((f"transformer.decoder.layers.{i}.linear1.weight", f"decoder.layers.{i}.fc1.weight"))
+ rename_keys.append((f"transformer.decoder.layers.{i}.linear1.bias", f"decoder.layers.{i}.fc1.bias"))
+ rename_keys.append((f"transformer.decoder.layers.{i}.linear2.weight", f"decoder.layers.{i}.fc2.weight"))
+ rename_keys.append((f"transformer.decoder.layers.{i}.linear2.bias", f"decoder.layers.{i}.fc2.bias"))
+ rename_keys.append(
+ (f"transformer.decoder.layers.{i}.norm1.weight", f"decoder.layers.{i}.self_attn_layer_norm.weight")
+ )
+ rename_keys.append(
+ (f"transformer.decoder.layers.{i}.norm1.bias", f"decoder.layers.{i}.self_attn_layer_norm.bias")
+ )
+ rename_keys.append(
+ (f"transformer.decoder.layers.{i}.norm2.weight", f"decoder.layers.{i}.encoder_attn_layer_norm.weight")
+ )
+ rename_keys.append(
+ (f"transformer.decoder.layers.{i}.norm2.bias", f"decoder.layers.{i}.encoder_attn_layer_norm.bias")
+ )
+ rename_keys.append(
+ (f"transformer.decoder.layers.{i}.norm3.weight", f"decoder.layers.{i}.final_layer_norm.weight")
+ )
+ rename_keys.append((f"transformer.decoder.layers.{i}.norm3.bias", f"decoder.layers.{i}.final_layer_norm.bias"))
+
+ # convolutional projection + query embeddings + layernorm of decoder + class and bounding box heads
+ rename_keys.extend(
+ [
+ ("input_proj.weight", "input_projection.weight"),
+ ("input_proj.bias", "input_projection.bias"),
+ ("query_embed.weight", "query_position_embeddings.weight"),
+ ("transformer.decoder.norm.weight", "decoder.layernorm.weight"),
+ ("transformer.decoder.norm.bias", "decoder.layernorm.bias"),
+ ("class_embed.weight", "class_labels_classifier.weight"),
+ ("class_embed.bias", "class_labels_classifier.bias"),
+ ("bbox_embed.layers.0.weight", "bbox_predictor.layers.0.weight"),
+ ("bbox_embed.layers.0.bias", "bbox_predictor.layers.0.bias"),
+ ("bbox_embed.layers.1.weight", "bbox_predictor.layers.1.weight"),
+ ("bbox_embed.layers.1.bias", "bbox_predictor.layers.1.bias"),
+ ("bbox_embed.layers.2.weight", "bbox_predictor.layers.2.weight"),
+ ("bbox_embed.layers.2.bias", "bbox_predictor.layers.2.bias"),
+ ]
+ )
+
+ return rename_keys
+
+
+def rename_key(state_dict, old, new):
+ val = state_dict.pop(old)
+ state_dict[new] = val
+
+
+def read_in_q_k_v(state_dict, is_panoptic=False):
+ prefix = ""
+ if is_panoptic:
+ prefix = "detr."
+
+ # first: transformer encoder
+ for i in range(6):
+ # read in weights + bias of input projection layer (in PyTorch's MultiHeadAttention, this is a single matrix + bias)
+ in_proj_weight = state_dict.pop(f"{prefix}transformer.encoder.layers.{i}.self_attn.in_proj_weight")
+ in_proj_bias = state_dict.pop(f"{prefix}transformer.encoder.layers.{i}.self_attn.in_proj_bias")
+ # next, add query, keys and values (in that order) to the state dict
+ state_dict[f"encoder.layers.{i}.self_attn.q_proj.weight"] = in_proj_weight[:256, :]
+ state_dict[f"encoder.layers.{i}.self_attn.q_proj.bias"] = in_proj_bias[:256]
+ state_dict[f"encoder.layers.{i}.self_attn.k_proj.weight"] = in_proj_weight[256:512, :]
+ state_dict[f"encoder.layers.{i}.self_attn.k_proj.bias"] = in_proj_bias[256:512]
+ state_dict[f"encoder.layers.{i}.self_attn.v_proj.weight"] = in_proj_weight[-256:, :]
+ state_dict[f"encoder.layers.{i}.self_attn.v_proj.bias"] = in_proj_bias[-256:]
+ # next: transformer decoder (which is a bit more complex because it also includes cross-attention)
+ for i in range(6):
+ # read in weights + bias of input projection layer of self-attention
+ in_proj_weight = state_dict.pop(f"{prefix}transformer.decoder.layers.{i}.self_attn.in_proj_weight")
+ in_proj_bias = state_dict.pop(f"{prefix}transformer.decoder.layers.{i}.self_attn.in_proj_bias")
+ # next, add query, keys and values (in that order) to the state dict
+ state_dict[f"decoder.layers.{i}.self_attn.q_proj.weight"] = in_proj_weight[:256, :]
+ state_dict[f"decoder.layers.{i}.self_attn.q_proj.bias"] = in_proj_bias[:256]
+ state_dict[f"decoder.layers.{i}.self_attn.k_proj.weight"] = in_proj_weight[256:512, :]
+ state_dict[f"decoder.layers.{i}.self_attn.k_proj.bias"] = in_proj_bias[256:512]
+ state_dict[f"decoder.layers.{i}.self_attn.v_proj.weight"] = in_proj_weight[-256:, :]
+ state_dict[f"decoder.layers.{i}.self_attn.v_proj.bias"] = in_proj_bias[-256:]
+ # read in weights + bias of input projection layer of cross-attention
+ in_proj_weight_cross_attn = state_dict.pop(
+ f"{prefix}transformer.decoder.layers.{i}.multihead_attn.in_proj_weight"
+ )
+ in_proj_bias_cross_attn = state_dict.pop(f"{prefix}transformer.decoder.layers.{i}.multihead_attn.in_proj_bias")
+ # next, add query, keys and values (in that order) of cross-attention to the state dict
+ state_dict[f"decoder.layers.{i}.encoder_attn.q_proj.weight"] = in_proj_weight_cross_attn[:256, :]
+ state_dict[f"decoder.layers.{i}.encoder_attn.q_proj.bias"] = in_proj_bias_cross_attn[:256]
+ state_dict[f"decoder.layers.{i}.encoder_attn.k_proj.weight"] = in_proj_weight_cross_attn[256:512, :]
+ state_dict[f"decoder.layers.{i}.encoder_attn.k_proj.bias"] = in_proj_bias_cross_attn[256:512]
+ state_dict[f"decoder.layers.{i}.encoder_attn.v_proj.weight"] = in_proj_weight_cross_attn[-256:, :]
+ state_dict[f"decoder.layers.{i}.encoder_attn.v_proj.bias"] = in_proj_bias_cross_attn[-256:]
+
+
+# We will verify our results on an image of cute cats
+def prepare_img():
+ url = "http://images.cocodataset.org/val2017/000000039769.jpg"
+ with httpx.stream("GET", url) as response:
+ image = Image.open(BytesIO(response.read()))
+
+ return image
+
+
+@torch.no_grad()
+def convert_detr_checkpoint(model_name, pytorch_dump_folder_path=None, push_to_hub=False):
+ """
+ Copy/paste/tweak model's weights to our DETR structure.
+ """
+
+ # load default config
+ config, is_panoptic = get_detr_config(model_name)
+
+ # load original model from torch hub
+ model_name_to_original_name = {
+ "detr-resnet-50": "detr_resnet50",
+ "detr-resnet-101": "detr_resnet101",
+ }
+ logger.info(f"Converting model {model_name}...")
+ detr = torch.hub.load("facebookresearch/detr", model_name_to_original_name[model_name], pretrained=True).eval()
+ state_dict = detr.state_dict()
+ # rename keys
+ for src, dest in create_rename_keys(config):
+ if is_panoptic:
+ src = "detr." + src
+ rename_key(state_dict, src, dest)
+ # query, key and value matrices need special treatment
+ read_in_q_k_v(state_dict, is_panoptic=is_panoptic)
+ # important: we need to prepend a prefix to each of the base model keys as the head models use different attributes for them
+ prefix = "detr.model." if is_panoptic else "model."
+ for key in state_dict.copy():
+ if is_panoptic:
+ if (
+ key.startswith("detr")
+ and not key.startswith("class_labels_classifier")
+ and not key.startswith("bbox_predictor")
+ ):
+ val = state_dict.pop(key)
+ state_dict["detr.model" + key[4:]] = val
+ elif "class_labels_classifier" in key or "bbox_predictor" in key:
+ val = state_dict.pop(key)
+ state_dict["detr." + key] = val
+ elif key.startswith("bbox_attention") or key.startswith("mask_head"):
+ continue
+ else:
+ val = state_dict.pop(key)
+ state_dict[prefix + key] = val
+ else:
+ if not key.startswith("class_labels_classifier") and not key.startswith("bbox_predictor"):
+ val = state_dict.pop(key)
+ state_dict[prefix + key] = val
+
+ # finally, create HuggingFace model and load state dict
+ model = DetrForSegmentation(config) if is_panoptic else DetrForObjectDetection(config)
+ model.load_state_dict(state_dict)
+ model.eval()
+
+ # verify our conversion on an image
+ format = "coco_panoptic" if is_panoptic else "coco_detection"
+ processor = DetrImageProcessor(format=format)
+
+ encoding = processor(images=prepare_img(), return_tensors="pt")
+ pixel_values = encoding["pixel_values"]
+
+ original_outputs = detr(pixel_values)
+ outputs = model(pixel_values)
+
+ assert torch.allclose(outputs.logits, original_outputs["pred_logits"], atol=1e-3)
+ assert torch.allclose(outputs.pred_boxes, original_outputs["pred_boxes"], atol=1e-3)
+ if is_panoptic:
+ assert torch.allclose(outputs.pred_masks, original_outputs["pred_masks"], atol=1e-4)
+ print("Looks ok!")
+
+ if pytorch_dump_folder_path is not None:
+ # Save model and image processor
+ logger.info(f"Saving PyTorch model and image processor to {pytorch_dump_folder_path}...")
+ Path(pytorch_dump_folder_path).mkdir(exist_ok=True)
+ model.save_pretrained(pytorch_dump_folder_path)
+ processor.save_pretrained(pytorch_dump_folder_path)
+
+ if push_to_hub:
+ # Upload model and image processor to the hub
+ logger.info("Uploading PyTorch model and image processor to the hub...")
+ model.push_to_hub(f"nielsr/{model_name}")
+ processor.push_to_hub(f"nielsr/{model_name}")
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+
+ parser.add_argument(
+ "--model_name",
+ default="detr-resnet-50",
+ type=str,
+ choices=["detr-resnet-50", "detr-resnet-101"],
+ help="Name of the DETR model you'd like to convert.",
+ )
+ parser.add_argument(
+ "--pytorch_dump_folder_path", default=None, type=str, help="Path to the folder to output PyTorch model."
+ )
+ parser.add_argument("--push_to_hub", action="store_true", help="Whether to push the model to the hub or not.")
+ args = parser.parse_args()
+ convert_detr_checkpoint(args.model_name, args.pytorch_dump_folder_path, args.push_to_hub)
diff --git a/third_party/transformers/src/transformers/models/detr/image_processing_detr.py b/third_party/transformers/src/transformers/models/detr/image_processing_detr.py
new file mode 100644
index 0000000000000000000000000000000000000000..e5cfa7ce14fbb0b47a565362a640500c04d2c198
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/detr/image_processing_detr.py
@@ -0,0 +1,1076 @@
+# Copyright 2024 The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Image processor class for DETR."""
+
+import pathlib
+from typing import Any, Optional
+
+import numpy as np
+import torch
+from torch import nn
+from torchvision.io import read_image
+from torchvision.transforms.v2 import functional as tvF
+
+from ...image_processing_backends import TorchvisionBackend
+from ...image_processing_utils import BatchFeature, get_size_dict
+from ...image_transforms import (
+ center_to_corners_format,
+ corners_to_center_format,
+ get_size_with_aspect_ratio,
+ safe_squeeze,
+)
+from ...image_utils import (
+ IMAGENET_DEFAULT_MEAN,
+ IMAGENET_DEFAULT_STD,
+ AnnotationFormat,
+ AnnotationType,
+ ChannelDimension,
+ ImageInput,
+ PILImageResampling,
+ SizeDict,
+ get_image_size,
+ get_image_size_for_max_height_width,
+ get_max_height_width,
+ validate_annotations,
+)
+from ...processing_utils import ImagesKwargs, Unpack
+from ...utils import (
+ TensorType,
+ auto_docstring,
+ logging,
+)
+
+
+logger = logging.get_logger(__name__)
+
+SUPPORTED_ANNOTATION_FORMATS = (AnnotationFormat.COCO_DETECTION, AnnotationFormat.COCO_PANOPTIC)
+
+
+class DetrImageProcessorKwargs(ImagesKwargs, total=False):
+ r"""
+ format (`str`, *optional*, defaults to `AnnotationFormat.COCO_DETECTION`):
+ Data format of the annotations. One of "coco_detection" or "coco_panoptic".
+ do_convert_annotations (`bool`, *optional*, defaults to `True`):
+ Controls whether to convert the annotations to the format expected by the DETR model. Converts the
+ bounding boxes to the format `(center_x, center_y, width, height)` and in the range `[0, 1]`.
+ Can be overridden by the `do_convert_annotations` parameter in the `preprocess` method.
+ """
+
+ format: str | AnnotationFormat
+ do_convert_annotations: bool
+
+
+def binary_mask_to_rle(mask):
+ """
+ Converts given binary mask of shape `(height, width)` to the run-length encoding (RLE) format.
+
+ Args:
+ mask (`torch.Tensor` or `numpy.array`):
+ A binary mask tensor of shape `(height, width)` where 0 denotes background and 1 denotes the target
+ segment_id or class_id.
+ Returns:
+ `List`: Run-length encoded list of the binary mask. Refer to COCO API for more information about the RLE
+ format.
+ """
+ from ...utils import is_torch_tensor
+
+ if is_torch_tensor(mask):
+ mask = mask.numpy()
+
+ pixels = mask.flatten()
+ pixels = np.concatenate([[0], pixels, [0]])
+ runs = np.where(pixels[1:] != pixels[:-1])[0] + 1
+ runs[1::2] -= runs[::2]
+ return list(runs)
+
+
+def convert_segmentation_to_rle(segmentation):
+ """
+ Converts given segmentation map of shape `(height, width)` to the run-length encoding (RLE) format.
+
+ Args:
+ segmentation (`torch.Tensor` or `numpy.array`):
+ A segmentation map of shape `(height, width)` where each value denotes a segment or class id.
+ Returns:
+ `list[List]`: A list of lists, where each list is the run-length encoding of a segment / class id.
+ """
+ segment_ids = torch.unique(segmentation)
+
+ run_length_encodings = []
+ for idx in segment_ids:
+ mask = torch.where(segmentation == idx, 1, 0)
+ rle = binary_mask_to_rle(mask)
+ run_length_encodings.append(rle)
+
+ return run_length_encodings
+
+
+def remove_low_and_no_objects(masks, scores, labels, object_mask_threshold, num_labels):
+ """
+ Binarize the given masks using `object_mask_threshold`, it returns the associated values of `masks`, `scores` and
+ `labels`.
+
+ Args:
+ masks (`torch.Tensor`):
+ A tensor of shape `(num_queries, height, width)`.
+ scores (`torch.Tensor`):
+ A tensor of shape `(num_queries)`.
+ labels (`torch.Tensor`):
+ A tensor of shape `(num_queries)`.
+ object_mask_threshold (`float`):
+ A number between 0 and 1 used to binarize the masks.
+ Raises:
+ `ValueError`: Raised when the first dimension doesn't match in all input tensors.
+ Returns:
+ `tuple[`torch.Tensor`, `torch.Tensor`, `torch.Tensor`]`: The `masks`, `scores` and `labels` without the region
+ < `object_mask_threshold`.
+ """
+ if not (masks.shape[0] == scores.shape[0] == labels.shape[0]):
+ raise ValueError("mask, scores and labels must have the same shape!")
+
+ to_keep = labels.ne(num_labels) & (scores > object_mask_threshold)
+
+ return masks[to_keep], scores[to_keep], labels[to_keep]
+
+
+def check_segment_validity(mask_labels, mask_probs, k, mask_threshold=0.5, overlap_mask_area_threshold=0.8):
+ # Get the mask associated with the k class
+ mask_k = mask_labels == k
+ mask_k_area = mask_k.sum()
+
+ # Compute the area of all the stuff in query k
+ original_area = (mask_probs[k] >= mask_threshold).sum()
+ mask_exists = mask_k_area > 0 and original_area > 0
+
+ # Eliminate disconnected tiny segments
+ if mask_exists:
+ area_ratio = mask_k_area / original_area
+ if not area_ratio.item() > overlap_mask_area_threshold:
+ mask_exists = False
+
+ return mask_exists, mask_k
+
+
+def compute_segments(
+ mask_probs,
+ pred_scores,
+ pred_labels,
+ mask_threshold: float = 0.5,
+ overlap_mask_area_threshold: float = 0.8,
+ label_ids_to_fuse: set[int] | None = None,
+ target_size: tuple[int, int] | None = None,
+):
+ height = mask_probs.shape[1] if target_size is None else target_size[0]
+ width = mask_probs.shape[2] if target_size is None else target_size[1]
+
+ segmentation = torch.zeros((height, width), dtype=torch.int32, device=mask_probs.device)
+ segments: list[dict] = []
+
+ if target_size is not None:
+ mask_probs = nn.functional.interpolate(
+ mask_probs.unsqueeze(0), size=target_size, mode="bilinear", align_corners=False
+ )[0]
+
+ current_segment_id = 0
+
+ # Weigh each mask by its prediction score
+ mask_probs *= pred_scores.view(-1, 1, 1)
+ mask_labels = mask_probs.argmax(0) # [height, width]
+
+ # Keep track of instances of each class
+ stuff_memory_list: dict[str, int] = {}
+ for k in range(pred_labels.shape[0]):
+ pred_class = pred_labels[k].item()
+ should_fuse = pred_class in label_ids_to_fuse
+
+ # Check if mask exists and large enough to be a segment
+ mask_exists, mask_k = check_segment_validity(
+ mask_labels, mask_probs, k, mask_threshold, overlap_mask_area_threshold
+ )
+
+ if mask_exists:
+ if pred_class in stuff_memory_list:
+ current_segment_id = stuff_memory_list[pred_class]
+ else:
+ current_segment_id += 1
+
+ # Add current object segment to final segmentation map
+ segmentation[mask_k] = current_segment_id
+ segment_score = round(pred_scores[k].item(), 6)
+ segments.append(
+ {
+ "id": current_segment_id,
+ "label_id": pred_class,
+ "was_fused": should_fuse,
+ "score": segment_score,
+ }
+ )
+ if should_fuse:
+ stuff_memory_list[pred_class] = current_segment_id
+
+ return segmentation, segments
+
+
+# inspired by https://github.com/facebookresearch/detr/blob/master/datasets/coco.py#L33
+def convert_coco_poly_to_mask(segmentations, height: int, width: int, device: torch.device) -> torch.Tensor:
+ """
+ Convert a COCO polygon annotation to a mask.
+
+ Args:
+ segmentations (`list[list[float]]`):
+ List of polygons, each polygon represented by a list of x-y coordinates.
+ height (`int`):
+ Height of the mask.
+ width (`int`):
+ Width of the mask.
+ """
+ try:
+ from pycocotools import mask as coco_mask
+ except ImportError:
+ raise ImportError("Pycocotools is not installed in your environment.")
+
+ masks = []
+ for polygons in segmentations:
+ rles = coco_mask.frPyObjects(polygons, height, width)
+ mask = coco_mask.decode(rles)
+ if len(mask.shape) < 3:
+ mask = mask[..., None]
+ mask = torch.as_tensor(mask, dtype=torch.uint8, device=device)
+ mask = torch.any(mask, axis=2)
+ masks.append(mask)
+ if masks:
+ masks = torch.stack(masks, axis=0)
+ else:
+ masks = torch.zeros((0, height, width), dtype=torch.uint8, device=device)
+
+ return masks
+
+
+# inspired by https://github.com/facebookresearch/detr/blob/master/datasets/coco.py#L50
+def prepare_coco_detection_annotation(
+ image,
+ target,
+ return_segmentation_masks: bool = False,
+ input_data_format: ChannelDimension | str | None = None,
+):
+ """
+ Convert the target in COCO format into the format expected by DETR.
+ """
+ image_height, image_width = image.size()[-2:]
+
+ image_id = target["image_id"]
+ image_id = torch.as_tensor([image_id], dtype=torch.int64, device=image.device)
+
+ # Get all COCO annotations for the given image.
+ annotations = target["annotations"]
+ classes = []
+ area = []
+ boxes = []
+ keypoints = []
+ for obj in annotations:
+ if "iscrowd" not in obj or obj["iscrowd"] == 0:
+ classes.append(obj["category_id"])
+ area.append(obj["area"])
+ boxes.append(obj["bbox"])
+ if "keypoints" in obj:
+ keypoints.append(obj["keypoints"])
+
+ classes = torch.as_tensor(classes, dtype=torch.int64, device=image.device)
+ area = torch.as_tensor(area, dtype=torch.float32, device=image.device)
+ iscrowd = torch.zeros_like(classes, dtype=torch.int64, device=image.device)
+ # guard against no boxes via resizing
+ boxes = torch.as_tensor(boxes, dtype=torch.float32, device=image.device).reshape(-1, 4)
+ boxes[:, 2:] += boxes[:, :2]
+ boxes[:, 0::2] = boxes[:, 0::2].clip(min=0, max=image_width)
+ boxes[:, 1::2] = boxes[:, 1::2].clip(min=0, max=image_height)
+
+ keep = (boxes[:, 3] > boxes[:, 1]) & (boxes[:, 2] > boxes[:, 0])
+
+ new_target = {
+ "image_id": image_id,
+ "class_labels": classes[keep],
+ "boxes": boxes[keep],
+ "area": area[keep],
+ "iscrowd": iscrowd[keep],
+ "orig_size": torch.as_tensor([int(image_height), int(image_width)], dtype=torch.int64, device=image.device),
+ }
+
+ if keypoints:
+ keypoints = torch.as_tensor(keypoints, dtype=torch.float32, device=image.device)
+ # Apply the keep mask here to filter the relevant annotations
+ keypoints = keypoints[keep]
+ num_keypoints = keypoints.shape[0]
+ keypoints = keypoints.reshape((-1, 3)) if num_keypoints else keypoints
+ new_target["keypoints"] = keypoints
+
+ if return_segmentation_masks:
+ segmentation_masks = [obj["segmentation"] for obj in annotations]
+ masks = convert_coco_poly_to_mask(segmentation_masks, image_height, image_width, device=image.device)
+ new_target["masks"] = masks[keep]
+
+ return new_target
+
+
+def masks_to_boxes(masks: torch.Tensor) -> torch.Tensor:
+ """
+ Compute the bounding boxes around the provided panoptic segmentation masks.
+
+ Args:
+ masks: masks in format `[number_masks, height, width]` where N is the number of masks
+
+ Returns:
+ boxes: bounding boxes in format `[number_masks, 4]` in xyxy format
+ """
+ if masks.numel() == 0:
+ return torch.zeros((0, 4), device=masks.device)
+
+ h, w = masks.shape[-2:]
+ y = torch.arange(0, h, dtype=torch.float32, device=masks.device)
+ x = torch.arange(0, w, dtype=torch.float32, device=masks.device)
+ # see https://github.com/pytorch/pytorch/issues/50276
+ y, x = torch.meshgrid(y, x, indexing="ij")
+
+ x_mask = masks * torch.unsqueeze(x, 0)
+ x_max = x_mask.view(x_mask.shape[0], -1).max(-1)[0]
+ x_min = (
+ torch.where(masks, x.unsqueeze(0), torch.tensor(1e8, device=masks.device)).view(masks.shape[0], -1).min(-1)[0]
+ )
+
+ y_mask = masks * torch.unsqueeze(y, 0)
+ y_max = y_mask.view(y_mask.shape[0], -1).max(-1)[0]
+ y_min = (
+ torch.where(masks, y.unsqueeze(0), torch.tensor(1e8, device=masks.device)).view(masks.shape[0], -1).min(-1)[0]
+ )
+
+ return torch.stack([x_min, y_min, x_max, y_max], 1)
+
+
+# 2 functions below adapted from https://github.com/cocodataset/panopticapi/blob/master/panopticapi/utils.py
+# Copyright (c) 2018, Alexander Kirillov
+# All rights reserved.
+def rgb_to_id(color):
+ """
+ Converts RGB color to unique ID.
+ """
+ if isinstance(color, torch.Tensor) and len(color.shape) == 3:
+ if color.dtype == torch.uint8:
+ color = color.to(torch.int32)
+ return color[:, :, 0] + 256 * color[:, :, 1] + 256 * 256 * color[:, :, 2]
+ return int(color[0] + 256 * color[1] + 256 * 256 * color[2])
+
+
+def prepare_coco_panoptic_annotation(
+ image: torch.Tensor,
+ target: dict,
+ masks_path: str | pathlib.Path,
+ return_masks: bool = True,
+ input_data_format: ChannelDimension | str = None,
+) -> dict:
+ """
+ Prepare a coco panoptic annotation for DETR.
+ """
+ image_height, image_width = get_image_size(image, channel_dim=input_data_format)
+ annotation_path = pathlib.Path(masks_path) / target["file_name"]
+
+ new_target = {}
+ new_target["image_id"] = torch.as_tensor(
+ [target["image_id"] if "image_id" in target else target["id"]], dtype=torch.int64, device=image.device
+ )
+ new_target["size"] = torch.as_tensor([image_height, image_width], dtype=torch.int64, device=image.device)
+ new_target["orig_size"] = torch.as_tensor([image_height, image_width], dtype=torch.int64, device=image.device)
+
+ if "segments_info" in target:
+ masks = read_image(annotation_path).permute(1, 2, 0).to(dtype=torch.int32, device=image.device)
+ masks = rgb_to_id(masks)
+
+ ids = torch.as_tensor([segment_info["id"] for segment_info in target["segments_info"]], device=image.device)
+ masks = masks == ids[:, None, None]
+ masks = masks.to(torch.bool)
+ if return_masks:
+ new_target["masks"] = masks
+ new_target["boxes"] = masks_to_boxes(masks)
+ new_target["class_labels"] = torch.as_tensor(
+ [segment_info["category_id"] for segment_info in target["segments_info"]],
+ dtype=torch.int64,
+ device=image.device,
+ )
+ new_target["iscrowd"] = torch.as_tensor(
+ [segment_info["iscrowd"] for segment_info in target["segments_info"]],
+ dtype=torch.int64,
+ device=image.device,
+ )
+ new_target["area"] = torch.as_tensor(
+ [segment_info["area"] for segment_info in target["segments_info"]],
+ dtype=torch.float32,
+ device=image.device,
+ )
+
+ return new_target
+
+
+@auto_docstring
+class DetrImageProcessor(TorchvisionBackend):
+ valid_kwargs = DetrImageProcessorKwargs
+ resample = PILImageResampling.BILINEAR
+ image_mean = IMAGENET_DEFAULT_MEAN
+ image_std = IMAGENET_DEFAULT_STD
+ format = AnnotationFormat.COCO_DETECTION
+ do_resize = True
+ do_rescale = True
+ do_normalize = True
+ do_pad = True
+ size = {"shortest_edge": 800, "longest_edge": 1333}
+ default_to_square = False
+ model_input_names = ["pixel_values", "pixel_mask"]
+
+ def __init__(self, **kwargs: Unpack[DetrImageProcessorKwargs]) -> None:
+ kwargs.setdefault("do_pad", kwargs.pop("pad_and_return_pixel_mask", self.do_pad))
+
+ size = kwargs.pop("size", None)
+ max_size = None if size is None else kwargs.pop("max_size", 1333)
+ size = size if size is not None else {"shortest_edge": 800, "longest_edge": 1333}
+ # Convert size dict for backwards compat with max_size parameter
+ kwargs["size"] = get_size_dict(size, max_size=max_size, default_to_square=False)
+
+ # Backwards compatibility
+ do_convert_annotations = kwargs.get("do_convert_annotations")
+ do_normalize = kwargs.get("do_normalize")
+ if do_convert_annotations is None and getattr(self, "do_convert_annotations", None) is None:
+ self.do_convert_annotations = do_normalize if do_normalize is not None else self.do_normalize
+
+ super().__init__(**kwargs)
+
+ def prepare_annotation(
+ self,
+ image: torch.Tensor,
+ target: dict,
+ format: AnnotationFormat | None = None,
+ return_segmentation_masks: bool | None = None,
+ masks_path: str | pathlib.Path | None = None,
+ input_data_format: str | ChannelDimension | None = None,
+ ) -> dict:
+ """
+ Prepare an annotation for feeding into DETR model.
+ """
+ format = format if format is not None else self.format
+
+ if format == AnnotationFormat.COCO_DETECTION:
+ return_segmentation_masks = False if return_segmentation_masks is None else return_segmentation_masks
+ target = prepare_coco_detection_annotation(
+ image, target, return_segmentation_masks, input_data_format=input_data_format
+ )
+ elif format == AnnotationFormat.COCO_PANOPTIC:
+ return_segmentation_masks = True if return_segmentation_masks is None else return_segmentation_masks
+ target = prepare_coco_panoptic_annotation(
+ image,
+ target,
+ masks_path=masks_path,
+ return_masks=return_segmentation_masks,
+ input_data_format=input_data_format,
+ )
+ else:
+ raise ValueError(f"Format {format} is not supported.")
+ return target
+
+ def resize(
+ self,
+ image: torch.Tensor,
+ size: SizeDict,
+ resample: Optional["PILImageResampling | tvF.InterpolationMode | int"] = None,
+ **kwargs,
+ ) -> torch.Tensor:
+ """
+ Resize the image to the given size. Size can be `min_size` (scalar) or `(height, width)` tuple. If size is an
+ int, smaller edge of the image will be matched to this number.
+
+ Args:
+ image (`torch.Tensor`):
+ Image to resize.
+ size (`SizeDict`):
+ Size of the image's `(height, width)` dimensions after resizing. Available options are:
+ - `{"height": int, "width": int}`: The image will be resized to the exact size `(height, width)`.
+ Do NOT keep the aspect ratio.
+ - `{"shortest_edge": int, "longest_edge": int}`: The image will be resized to a maximum size respecting
+ the aspect ratio and keeping the shortest edge less or equal to `shortest_edge` and the longest edge
+ less or equal to `longest_edge`.
+ - `{"max_height": int, "max_width": int}`: The image will be resized to the maximum size respecting the
+ aspect ratio and keeping the height less or equal to `max_height` and the width less or equal to
+ `max_width`.
+ resample (`PILImageResampling | tvF.InterpolationMode | int`, *optional*, defaults to `PILImageResampling.BILINEAR`):
+ Resampling filter to use if resizing the image.
+ """
+ if size.shortest_edge and size.longest_edge:
+ # Resize the image so that the shortest edge or the longest edge is of the given size
+ # while maintaining the aspect ratio of the original image.
+ new_size = get_size_with_aspect_ratio(image.shape[-2:], size.shortest_edge, size.longest_edge)
+ elif size.max_height and size.max_width:
+ new_size = get_image_size_for_max_height_width(image.shape[-2:], size.max_height, size.max_width)
+ elif size.height and size.width:
+ new_size = (size.height, size.width)
+ else:
+ raise ValueError(
+ f"Size must contain 'height' and 'width' keys or 'shortest_edge' and 'longest_edge' keys. Got {size}."
+ )
+
+ image = super().resize(
+ image, size=SizeDict(height=new_size[0], width=new_size[1]), resample=resample, **kwargs
+ )
+ return image
+
+ def resize_annotation(
+ self,
+ annotation: dict[str, Any],
+ orig_size: tuple[int, int],
+ target_size: tuple[int, int],
+ threshold: float = 0.5,
+ resample: Optional["PILImageResampling | tvF.InterpolationMode | int"] = PILImageResampling.NEAREST,
+ ):
+ """
+ Resizes an annotation to a target size.
+
+ Args:
+ annotation (`dict[str, Any]`):
+ The annotation dictionary.
+ orig_size (`tuple[int, int]`):
+ The original size of the input image.
+ target_size (`tuple[int, int]`):
+ The target size of the image, as returned by the preprocessing `resize` step.
+ threshold (`float`, *optional*, defaults to 0.5):
+ The threshold used to binarize the segmentation masks.
+ resample (`PILImageResampling | tvF.InterpolationMode | int`, defaults to `tvF.InterpolationMode.NEAREST_EXACT`):
+ The resampling filter to use when resizing the masks.
+ """
+ ratio_height, ratio_width = [target / orig for target, orig in zip(target_size, orig_size)]
+
+ new_annotation = {}
+ new_annotation["size"] = target_size
+
+ for key, value in annotation.items():
+ if key == "boxes":
+ boxes = value
+ scaled_boxes = boxes * torch.as_tensor(
+ [ratio_width, ratio_height, ratio_width, ratio_height], dtype=torch.float32, device=boxes.device
+ )
+ new_annotation["boxes"] = scaled_boxes
+ elif key == "area":
+ area = value
+ scaled_area = area * (ratio_width * ratio_height)
+ new_annotation["area"] = scaled_area
+ elif key == "masks":
+ masks = value[:, None]
+ masks = [
+ super(DetrImageProcessor, self).resize(
+ mask, size=SizeDict(height=target_size[0], width=target_size[1]), resample=resample
+ )
+ for mask in masks
+ ]
+ masks = torch.stack(masks).to(torch.float32)
+ masks = masks[:, 0] > threshold
+ new_annotation["masks"] = masks
+ elif key == "size":
+ new_annotation["size"] = target_size
+ else:
+ new_annotation[key] = value
+
+ return new_annotation
+
+ def normalize_annotation(self, annotation: dict, image_size: tuple[int, int]) -> dict:
+ image_height, image_width = image_size
+ norm_annotation = {}
+ for key, value in annotation.items():
+ if key == "boxes":
+ boxes = value
+ boxes = corners_to_center_format(boxes)
+ boxes /= torch.as_tensor(
+ [image_width, image_height, image_width, image_height], dtype=torch.float32, device=boxes.device
+ )
+ norm_annotation[key] = boxes
+ else:
+ norm_annotation[key] = value
+ return norm_annotation
+
+ def _update_annotation_for_padded_image(
+ self,
+ annotation: dict,
+ input_image_size: tuple[int, int],
+ output_image_size: tuple[int, int],
+ padding,
+ update_bboxes,
+ ) -> dict:
+ """
+ Update the annotation for a padded image.
+ """
+ new_annotation = {}
+ new_annotation["size"] = output_image_size
+ ratio_height, ratio_width = (input / output for output, input in zip(output_image_size, input_image_size))
+
+ for key, value in annotation.items():
+ if key == "masks":
+ masks = value
+ masks = tvF.pad(
+ masks,
+ padding,
+ fill=0,
+ )
+ masks = safe_squeeze(masks, 1)
+ new_annotation["masks"] = masks
+ elif key == "boxes" and update_bboxes:
+ boxes = value
+ boxes *= torch.as_tensor([ratio_width, ratio_height, ratio_width, ratio_height], device=boxes.device)
+ new_annotation["boxes"] = boxes
+ elif key == "size":
+ new_annotation["size"] = output_image_size
+ else:
+ new_annotation[key] = value
+ return new_annotation
+
+ def pad(
+ self,
+ image: torch.Tensor,
+ padded_size: tuple[int, int],
+ annotation: dict[str, Any] | None = None,
+ update_bboxes: bool = True,
+ fill: int = 0,
+ ):
+ original_size = image.size()[-2:]
+ padding_bottom = padded_size[0] - original_size[0]
+ padding_right = padded_size[1] - original_size[1]
+ if padding_bottom < 0 or padding_right < 0:
+ raise ValueError(
+ f"Padding dimensions are negative. Please make sure that the padded size is larger than the "
+ f"original size. Got padded size: {padded_size}, original size: {original_size}."
+ )
+ if original_size != padded_size:
+ padding = [0, 0, padding_right, padding_bottom]
+ image = tvF.pad(image, padding, fill=fill)
+ if annotation is not None:
+ annotation = self._update_annotation_for_padded_image(
+ annotation, original_size, padded_size, padding, update_bboxes
+ )
+
+ # Make a pixel mask for the image, where 1 indicates a valid pixel and 0 indicates padding.
+ pixel_mask = torch.zeros(padded_size, dtype=torch.int64, device=image.device)
+ pixel_mask[: original_size[0], : original_size[1]] = 1
+
+ return image, pixel_mask, annotation
+
+ @auto_docstring
+ def preprocess(
+ self,
+ images: ImageInput,
+ annotations: AnnotationType | list[AnnotationType] | None = None,
+ return_segmentation_masks: bool | None = None,
+ masks_path: str | pathlib.Path | None = None,
+ **kwargs: Unpack[DetrImageProcessorKwargs],
+ ) -> BatchFeature:
+ r"""
+ annotations (`AnnotationType` or `list[AnnotationType]`, *optional*):
+ Annotations to transform according to the padding that is applied to the images.
+ return_segmentation_masks (`bool`, *optional*, defaults to `self.return_segmentation_masks`):
+ Whether to return segmentation masks.
+ masks_path (`str` or `pathlib.Path`, *optional*):
+ Path to the directory containing the segmentation masks.
+ """
+ return super().preprocess(images, annotations, return_segmentation_masks, masks_path, **kwargs)
+
+ def _preprocess(
+ self,
+ images: list["torch.Tensor"],
+ annotations: AnnotationType | list[AnnotationType] | None,
+ return_segmentation_masks: bool,
+ masks_path: str | pathlib.Path | None,
+ do_resize: bool,
+ size: SizeDict,
+ resample: "PILImageResampling | tvF.InterpolationMode | int | None",
+ do_rescale: bool,
+ rescale_factor: float,
+ do_normalize: bool,
+ do_convert_annotations: bool,
+ image_mean: float | list[float] | None,
+ image_std: float | list[float] | None,
+ do_pad: bool,
+ pad_size: SizeDict | None,
+ format: str | AnnotationFormat | None,
+ return_tensors: str | TensorType | None,
+ **kwargs,
+ ) -> BatchFeature:
+ """
+ Preprocess an image or a batch of images so that it can be used by the model.
+ """
+ if annotations is not None and isinstance(annotations, dict):
+ annotations = [annotations]
+
+ if annotations is not None and len(images) != len(annotations):
+ raise ValueError(
+ f"The number of images ({len(images)}) and annotations ({len(annotations)}) do not match."
+ )
+
+ format = AnnotationFormat(format)
+ if annotations is not None:
+ validate_annotations(format, SUPPORTED_ANNOTATION_FORMATS, annotations)
+
+ if (
+ masks_path is not None
+ and format == AnnotationFormat.COCO_PANOPTIC
+ and not isinstance(masks_path, (pathlib.Path, str))
+ ):
+ raise ValueError(
+ "The path to the directory containing the mask PNG files should be provided as a"
+ f" `pathlib.Path` or string object, but is {type(masks_path)} instead."
+ )
+
+ data = {}
+
+ processed_images = []
+ processed_annotations = []
+ pixel_masks = [] # Initialize pixel_masks here
+ for image, annotation in zip(images, annotations if annotations is not None else [None] * len(images)):
+ # prepare (COCO annotations as a list of Dict -> DETR target as a single Dict per image)
+ if annotations is not None:
+ annotation = self.prepare_annotation(
+ image,
+ annotation,
+ format,
+ return_segmentation_masks=return_segmentation_masks,
+ masks_path=masks_path,
+ input_data_format=ChannelDimension.FIRST,
+ )
+
+ if do_resize:
+ resized_image = self.resize(image, size=size, resample=resample)
+ if annotations is not None:
+ annotation = self.resize_annotation(
+ annotation,
+ orig_size=image.size()[-2:],
+ target_size=resized_image.size()[-2:],
+ )
+ image = resized_image
+ # Fused rescale and normalize
+ image = self.rescale_and_normalize(image, do_rescale, rescale_factor, do_normalize, image_mean, image_std)
+ if do_convert_annotations and annotations is not None:
+ annotation = self.normalize_annotation(annotation, get_image_size(image, ChannelDimension.FIRST))
+
+ processed_images.append(image)
+ processed_annotations.append(annotation)
+ images = processed_images
+ annotations = processed_annotations if annotations is not None else None
+
+ if do_pad:
+ # depends on all resized image shapes so we need another loop
+ if pad_size is not None:
+ padded_size = (pad_size.height, pad_size.width)
+ else:
+ padded_size = get_max_height_width(images)
+
+ padded_images = []
+ padded_annotations = []
+ for image, annotation in zip(images, annotations if annotations is not None else [None] * len(images)):
+ # Pads images and returns their mask: {'pixel_values': ..., 'pixel_mask': ...}
+ if padded_size == image.size()[-2:]:
+ padded_images.append(image)
+ pixel_masks.append(torch.ones(padded_size, dtype=torch.int64, device=image.device))
+ padded_annotations.append(annotation)
+ continue
+ image, pixel_mask, annotation = self.pad(
+ image, padded_size, annotation=annotation, update_bboxes=do_convert_annotations
+ )
+ padded_images.append(image)
+ padded_annotations.append(annotation)
+ pixel_masks.append(pixel_mask)
+ images = padded_images
+ annotations = padded_annotations if annotations is not None else None
+ data.update({"pixel_mask": torch.stack(pixel_masks, dim=0)})
+
+ data.update({"pixel_values": torch.stack(images, dim=0)})
+ encoded_inputs = BatchFeature(data, tensor_type=return_tensors)
+ if annotations is not None:
+ encoded_inputs["labels"] = [
+ BatchFeature(annotation, tensor_type=return_tensors) for annotation in annotations
+ ]
+ return encoded_inputs
+
+ # Copied from transformers.models.detr.image_processing_detr.DetrImageProcessor.post_process_object_detection
+ def post_process_object_detection(
+ self, outputs, threshold: float = 0.5, target_sizes: TensorType | list[tuple] | None = None
+ ):
+ """
+ Converts the raw output of [`DetrForObjectDetection`] into final bounding boxes in (top_left_x, top_left_y,
+ bottom_right_x, bottom_right_y) format. Only supports PyTorch.
+
+ Args:
+ outputs ([`DetrObjectDetectionOutput`]):
+ Raw outputs of the model.
+ threshold (`float`, *optional*):
+ Score threshold to keep object detection predictions.
+ target_sizes (`torch.Tensor` or `list[tuple[int, int]]`, *optional*):
+ Tensor of shape `(batch_size, 2)` or list of tuples (`tuple[int, int]`) containing the target size
+ `(height, width)` of each image in the batch. If unset, predictions will not be resized.
+ Returns:
+ `list[Dict]`: A list of dictionaries, each dictionary containing the scores, labels and boxes for an image
+ in the batch as predicted by the model.
+ """
+ out_logits, out_bbox = outputs.logits, outputs.pred_boxes
+
+ if target_sizes is not None:
+ if len(out_logits) != len(target_sizes):
+ raise ValueError(
+ "Make sure that you pass in as many target sizes as the batch dimension of the logits"
+ )
+
+ prob = nn.functional.softmax(out_logits, -1)
+ scores, labels = prob[..., :-1].max(-1)
+
+ # Convert to [x0, y0, x1, y1] format
+ boxes = center_to_corners_format(out_bbox)
+
+ # Convert from relative [0, 1] to absolute [0, height] coordinates
+ if target_sizes is not None:
+ if isinstance(target_sizes, list):
+ img_h = torch.Tensor([i[0] for i in target_sizes])
+ img_w = torch.Tensor([i[1] for i in target_sizes])
+ else:
+ img_h, img_w = target_sizes.unbind(1)
+
+ scale_fct = torch.stack([img_w, img_h, img_w, img_h], dim=1).to(boxes.device)
+ boxes = boxes * scale_fct[:, None, :]
+
+ results = []
+ for s, l, b in zip(scores, labels, boxes):
+ score = s[s > threshold]
+ label = l[s > threshold]
+ box = b[s > threshold]
+ results.append({"scores": score, "labels": label, "boxes": box})
+
+ return results
+
+ def post_process_semantic_segmentation(self, outputs, target_sizes: list[tuple[int, int]] | None = None):
+ """
+ Converts the output of [`DetrForSegmentation`] into semantic segmentation maps. Only supports PyTorch.
+
+ Args:
+ outputs ([`DetrForSegmentation`]):
+ Raw outputs of the model.
+ target_sizes (`list[tuple[int, int]]`, *optional*):
+ A list of tuples (`tuple[int, int]`) containing the target size (height, width) of each image in the
+ batch. If unset, predictions will not be resized.
+ Returns:
+ `list[torch.Tensor]`:
+ A list of length `batch_size`, where each item is a semantic segmentation map of shape (height, width)
+ corresponding to the target_sizes entry (if `target_sizes` is specified). Each entry of each
+ `torch.Tensor` correspond to a semantic class id.
+ """
+ class_queries_logits = outputs.logits # [batch_size, num_queries, num_classes+1]
+ masks_queries_logits = outputs.pred_masks # [batch_size, num_queries, height, width]
+
+ # Remove the null class `[..., :-1]`
+ masks_classes = class_queries_logits.softmax(dim=-1)[..., :-1]
+ masks_probs = masks_queries_logits.sigmoid() # [batch_size, num_queries, height, width]
+
+ # Semantic segmentation logits of shape (batch_size, num_classes, height, width)
+ segmentation = torch.einsum("bqc, bqhw -> bchw", masks_classes, masks_probs)
+ batch_size = class_queries_logits.shape[0]
+
+ # Resize logits and compute semantic segmentation maps
+ if target_sizes is not None:
+ if batch_size != len(target_sizes):
+ raise ValueError(
+ "Make sure that you pass in as many target sizes as the batch dimension of the logits"
+ )
+
+ semantic_segmentation = []
+ for idx in range(batch_size):
+ resized_logits = nn.functional.interpolate(
+ segmentation[idx].unsqueeze(dim=0), size=target_sizes[idx], mode="bilinear", align_corners=False
+ )
+ semantic_map = resized_logits[0].argmax(dim=0)
+ semantic_segmentation.append(semantic_map)
+ else:
+ semantic_segmentation = segmentation.argmax(dim=1)
+ semantic_segmentation = [semantic_segmentation[i] for i in range(semantic_segmentation.shape[0])]
+
+ return semantic_segmentation
+
+ def post_process_instance_segmentation(
+ self,
+ outputs,
+ threshold: float = 0.5,
+ mask_threshold: float = 0.5,
+ overlap_mask_area_threshold: float = 0.8,
+ target_sizes: list[tuple[int, int]] | None = None,
+ return_coco_annotation: bool | None = False,
+ ) -> list[dict]:
+ """
+ Converts the output of [`DetrForSegmentation`] into instance segmentation predictions. Only supports PyTorch.
+
+ Args:
+ outputs ([`DetrForSegmentation`]):
+ Raw outputs of the model.
+ threshold (`float`, *optional*, defaults to 0.5):
+ The probability score threshold to keep predicted instance masks.
+ mask_threshold (`float`, *optional*, defaults to 0.5):
+ Threshold to use when turning the predicted masks into binary values.
+ overlap_mask_area_threshold (`float`, *optional*, defaults to 0.8):
+ The overlap mask area threshold to merge or discard small disconnected parts within each binary
+ instance mask.
+ target_sizes (`list[Tuple]`, *optional*):
+ List of length (batch_size), where each list item (`tuple[int, int]]`) corresponds to the requested
+ final size (height, width) of each prediction. If unset, predictions will not be resized.
+ return_coco_annotation (`bool`, *optional*):
+ Defaults to `False`. If set to `True`, segmentation maps are returned in COCO run-length encoding (RLE)
+ format.
+ Returns:
+ `list[Dict]`: A list of dictionaries, one per image, each dictionary containing two keys:
+ - **segmentation** -- A tensor of shape `(height, width)` where each pixel represents a `segment_id` or
+ `list[List]` run-length encoding (RLE) of the segmentation map if return_coco_annotation is set to
+ `True`. Set to `None` if no mask if found above `threshold`.
+ - **segments_info** -- A dictionary that contains additional information on each segment.
+ - **id** -- An integer representing the `segment_id`.
+ - **label_id** -- An integer representing the label / semantic class id corresponding to `segment_id`.
+ - **score** -- Prediction score of segment with `segment_id`.
+ """
+ class_queries_logits = outputs.logits # [batch_size, num_queries, num_classes+1]
+ masks_queries_logits = outputs.pred_masks # [batch_size, num_queries, height, width]
+
+ batch_size = class_queries_logits.shape[0]
+ num_labels = class_queries_logits.shape[-1] - 1
+
+ mask_probs = masks_queries_logits.sigmoid() # [batch_size, num_queries, height, width]
+
+ # Predicted label and score of each query (batch_size, num_queries)
+ pred_scores, pred_labels = nn.functional.softmax(class_queries_logits, dim=-1).max(-1)
+
+ # Loop over items in batch size
+ results: list[dict[str, TensorType]] = []
+
+ for i in range(batch_size):
+ mask_probs_item, pred_scores_item, pred_labels_item = remove_low_and_no_objects(
+ mask_probs[i], pred_scores[i], pred_labels[i], threshold, num_labels
+ )
+
+ # No mask found
+ if mask_probs_item.shape[0] <= 0:
+ height, width = target_sizes[i] if target_sizes is not None else mask_probs_item.shape[1:]
+ segmentation = torch.zeros((height, width)) - 1
+ results.append({"segmentation": segmentation, "segments_info": []})
+ continue
+
+ # Get segmentation map and segment information of batch item
+ target_size = target_sizes[i] if target_sizes is not None else None
+ segmentation, segments = compute_segments(
+ mask_probs=mask_probs_item,
+ pred_scores=pred_scores_item,
+ pred_labels=pred_labels_item,
+ mask_threshold=mask_threshold,
+ overlap_mask_area_threshold=overlap_mask_area_threshold,
+ label_ids_to_fuse=[],
+ target_size=target_size,
+ )
+
+ # Return segmentation map in run-length encoding (RLE) format
+ if return_coco_annotation:
+ segmentation = convert_segmentation_to_rle(segmentation)
+
+ results.append({"segmentation": segmentation, "segments_info": segments})
+ return results
+
+ def post_process_panoptic_segmentation(
+ self,
+ outputs,
+ threshold: float = 0.5,
+ mask_threshold: float = 0.5,
+ overlap_mask_area_threshold: float = 0.8,
+ label_ids_to_fuse: set[int] | None = None,
+ target_sizes: list[tuple[int, int]] | None = None,
+ ) -> list[dict]:
+ """
+ Converts the output of [`DetrForSegmentation`] into image panoptic segmentation predictions. Only supports
+ PyTorch.
+
+ Args:
+ outputs ([`DetrForSegmentation`]):
+ The outputs from [`DetrForSegmentation`].
+ threshold (`float`, *optional*, defaults to 0.5):
+ The probability score threshold to keep predicted instance masks.
+ mask_threshold (`float`, *optional*, defaults to 0.5):
+ Threshold to use when turning the predicted masks into binary values.
+ overlap_mask_area_threshold (`float`, *optional*, defaults to 0.8):
+ The overlap mask area threshold to merge or discard small disconnected parts within each binary
+ instance mask.
+ label_ids_to_fuse (`Set[int]`, *optional*):
+ The labels in this state will have all their instances be fused together. For instance we could say
+ there can only be one sky in an image, but several persons, so the label ID for sky would be in that
+ set, but not the one for person.
+ target_sizes (`list[Tuple]`, *optional*):
+ List of length (batch_size), where each list item (`tuple[int, int]]`) corresponds to the requested
+ final size (height, width) of each prediction in batch. If unset, predictions will not be resized.
+ Returns:
+ `list[Dict]`: A list of dictionaries, one per image, each dictionary containing two keys:
+ - **segmentation** -- a tensor of shape `(height, width)` where each pixel represents a `segment_id` or
+ `None` if no mask if found above `threshold`. If `target_sizes` is specified, segmentation is resized to
+ the corresponding `target_sizes` entry.
+ - **segments_info** -- A dictionary that contains additional information on each segment.
+ - **id** -- an integer representing the `segment_id`.
+ - **label_id** -- An integer representing the label / semantic class id corresponding to `segment_id`.
+ - **was_fused** -- a boolean, `True` if `label_id` was in `label_ids_to_fuse`, `False` otherwise.
+ Multiple instances of the same class / label were fused and assigned a single `segment_id`.
+ - **score** -- Prediction score of segment with `segment_id`.
+ """
+
+ if label_ids_to_fuse is None:
+ logger.warning_once("`label_ids_to_fuse` unset. No instance will be fused.")
+ label_ids_to_fuse = set()
+
+ class_queries_logits = outputs.logits # [batch_size, num_queries, num_classes+1]
+ masks_queries_logits = outputs.pred_masks # [batch_size, num_queries, height, width]
+
+ batch_size = class_queries_logits.shape[0]
+ num_labels = class_queries_logits.shape[-1] - 1
+
+ mask_probs = masks_queries_logits.sigmoid() # [batch_size, num_queries, height, width]
+
+ # Predicted label and score of each query (batch_size, num_queries)
+ pred_scores, pred_labels = nn.functional.softmax(class_queries_logits, dim=-1).max(-1)
+
+ # Loop over items in batch size
+ results: list[dict[str, TensorType]] = []
+
+ for i in range(batch_size):
+ mask_probs_item, pred_scores_item, pred_labels_item = remove_low_and_no_objects(
+ mask_probs[i], pred_scores[i], pred_labels[i], threshold, num_labels
+ )
+
+ # No mask found
+ if mask_probs_item.shape[0] <= 0:
+ height, width = target_sizes[i] if target_sizes is not None else mask_probs_item.shape[1:]
+ segmentation = torch.zeros((height, width)) - 1
+ results.append({"segmentation": segmentation, "segments_info": []})
+ continue
+
+ # Get segmentation map and segment information of batch item
+ target_size = target_sizes[i] if target_sizes is not None else None
+ segmentation, segments = compute_segments(
+ mask_probs=mask_probs_item,
+ pred_scores=pred_scores_item,
+ pred_labels=pred_labels_item,
+ mask_threshold=mask_threshold,
+ overlap_mask_area_threshold=overlap_mask_area_threshold,
+ label_ids_to_fuse=label_ids_to_fuse,
+ target_size=target_size,
+ )
+
+ results.append({"segmentation": segmentation, "segments_info": segments})
+ return results
+
+
+__all__ = ["DetrImageProcessor"]
diff --git a/third_party/transformers/src/transformers/models/detr/image_processing_pil_detr.py b/third_party/transformers/src/transformers/models/detr/image_processing_pil_detr.py
new file mode 100644
index 0000000000000000000000000000000000000000..14c5769549d8313bb42230794c8b62536cdd81a2
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/detr/image_processing_pil_detr.py
@@ -0,0 +1,1136 @@
+# Copyright 2024 The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Image processor class for DETR."""
+
+import pathlib
+from typing import Any, Optional
+
+import numpy as np
+
+from ...image_processing_backends import PilBackend
+from ...image_processing_utils import BatchFeature
+from ...image_transforms import (
+ PaddingMode,
+ center_to_corners_format,
+ corners_to_center_format,
+ get_size_with_aspect_ratio,
+ pad,
+ resize,
+ safe_squeeze,
+)
+from ...image_utils import (
+ IMAGENET_DEFAULT_MEAN,
+ IMAGENET_DEFAULT_STD,
+ AnnotationFormat,
+ AnnotationType,
+ ChannelDimension,
+ ImageInput,
+ PILImageResampling,
+ SizeDict,
+ get_image_size,
+ get_image_size_for_max_height_width,
+ get_max_height_width,
+ validate_annotations,
+)
+from ...processing_utils import ImagesKwargs, Unpack
+from ...utils import (
+ TensorType,
+ auto_docstring,
+ is_torch_available,
+ is_vision_available,
+ logging,
+)
+from ...utils.import_utils import requires
+
+
+if is_vision_available():
+ import PIL.Image
+
+logger = logging.get_logger(__name__)
+
+SUPPORTED_ANNOTATION_FORMATS = (AnnotationFormat.COCO_DETECTION, AnnotationFormat.COCO_PANOPTIC)
+
+
+# inspired by https://github.com/facebookresearch/detr/blob/master/datasets/coco.py#L33
+def convert_coco_poly_to_mask(segmentations, height: int, width: int) -> np.ndarray:
+ """
+ Convert a COCO polygon annotation to a mask.
+
+ Args:
+ segmentations (`list[list[float]]`):
+ List of polygons, each polygon represented by a list of x-y coordinates.
+ height (`int`):
+ Height of the mask.
+ width (`int`):
+ Width of the mask.
+ """
+ try:
+ from pycocotools import mask as coco_mask
+ except ImportError:
+ raise ImportError("Pycocotools is not installed in your environment.")
+
+ masks = []
+ for polygons in segmentations:
+ rles = coco_mask.frPyObjects(polygons, height, width)
+ mask = coco_mask.decode(rles)
+ if len(mask.shape) < 3:
+ mask = mask[..., None]
+ mask = np.asarray(mask, dtype=np.uint8)
+ mask = np.any(mask, axis=2)
+ masks.append(mask)
+ if masks:
+ masks = np.stack(masks, axis=0)
+ else:
+ masks = np.zeros((0, height, width), dtype=np.uint8)
+
+ return masks
+
+
+# inspired by https://github.com/facebookresearch/detr/blob/master/datasets/coco.py#L50
+def prepare_coco_detection_annotation(
+ image,
+ target,
+ return_segmentation_masks: bool = False,
+ input_data_format: ChannelDimension | str | None = None,
+):
+ """
+ Convert the target in COCO format into the format expected by DETR.
+ """
+ image_height, image_width = get_image_size(image, channel_dim=input_data_format)
+
+ image_id = target["image_id"]
+ image_id = np.asarray([image_id], dtype=np.int64)
+
+ # Get all COCO annotations for the given image.
+ annotations = target["annotations"]
+ annotations = [obj for obj in annotations if "iscrowd" not in obj or obj["iscrowd"] == 0]
+
+ classes = [obj["category_id"] for obj in annotations]
+ classes = np.asarray(classes, dtype=np.int64)
+
+ # for conversion to coco api
+ area = np.asarray([obj["area"] for obj in annotations], dtype=np.float32)
+ iscrowd = np.asarray([obj.get("iscrowd", 0) for obj in annotations], dtype=np.int64)
+
+ boxes = [obj["bbox"] for obj in annotations]
+ # guard against no boxes via resizing
+ boxes = np.asarray(boxes, dtype=np.float32).reshape(-1, 4)
+ boxes[:, 2:] += boxes[:, :2]
+ boxes[:, 0::2] = boxes[:, 0::2].clip(min=0, max=image_width)
+ boxes[:, 1::2] = boxes[:, 1::2].clip(min=0, max=image_height)
+
+ keep = (boxes[:, 3] > boxes[:, 1]) & (boxes[:, 2] > boxes[:, 0])
+
+ new_target = {}
+ new_target["image_id"] = image_id
+ new_target["class_labels"] = classes[keep]
+ new_target["boxes"] = boxes[keep]
+ new_target["area"] = area[keep]
+ new_target["iscrowd"] = iscrowd[keep]
+ new_target["orig_size"] = np.asarray([int(image_height), int(image_width)], dtype=np.int64)
+
+ if annotations and "keypoints" in annotations[0]:
+ keypoints = [obj["keypoints"] for obj in annotations]
+ # Converting the filtered keypoints list to a numpy array
+ keypoints = np.asarray(keypoints, dtype=np.float32)
+ # Apply the keep mask here to filter the relevant annotations
+ keypoints = keypoints[keep]
+ num_keypoints = keypoints.shape[0]
+ keypoints = keypoints.reshape((-1, 3)) if num_keypoints else keypoints
+ new_target["keypoints"] = keypoints
+
+ if return_segmentation_masks:
+ segmentation_masks = [obj["segmentation"] for obj in annotations]
+ masks = convert_coco_poly_to_mask(segmentation_masks, image_height, image_width)
+ new_target["masks"] = masks[keep]
+
+ return new_target
+
+
+def masks_to_boxes(masks: np.ndarray) -> np.ndarray:
+ """
+ Compute the bounding boxes around the provided panoptic segmentation masks.
+
+ Args:
+ masks: masks in format `[number_masks, height, width]` where N is the number of masks
+
+ Returns:
+ boxes: bounding boxes in format `[number_masks, 4]` in xyxy format
+ """
+ if masks.size == 0:
+ return np.zeros((0, 4))
+
+ h, w = masks.shape[-2:]
+ y = np.arange(0, h, dtype=np.float32)
+ x = np.arange(0, w, dtype=np.float32)
+ # see https://github.com/pytorch/pytorch/issues/50276
+ y, x = np.meshgrid(y, x, indexing="ij")
+
+ x_mask = masks * np.expand_dims(x, axis=0)
+ x_max = x_mask.reshape(x_mask.shape[0], -1).max(-1)
+ x = np.ma.array(x_mask, mask=~(np.array(masks, dtype=bool)))
+ x_min = x.filled(fill_value=1e8)
+ x_min = x_min.reshape(x_min.shape[0], -1).min(-1)
+
+ y_mask = masks * np.expand_dims(y, axis=0)
+ y_max = y_mask.reshape(x_mask.shape[0], -1).max(-1)
+ y = np.ma.array(y_mask, mask=~(np.array(masks, dtype=bool)))
+ y_min = y.filled(fill_value=1e8)
+ y_min = y_min.reshape(y_min.shape[0], -1).min(-1)
+
+ return np.stack([x_min, y_min, x_max, y_max], 1)
+
+
+# 2 functions below adapted from https://github.com/cocodataset/panopticapi/blob/master/panopticapi/utils.py
+# Copyright (c) 2018, Alexander Kirillov
+# All rights reserved.
+def rgb_to_id(color):
+ """
+ Converts RGB color to unique ID.
+ """
+ if isinstance(color, np.ndarray) and len(color.shape) == 3:
+ if color.dtype == np.uint8:
+ color = color.astype(np.int32)
+ return color[:, :, 0] + 256 * color[:, :, 1] + 256 * 256 * color[:, :, 2]
+ return int(color[0] + 256 * color[1] + 256 * 256 * color[2])
+
+
+def prepare_coco_panoptic_annotation(
+ image: np.ndarray,
+ target: dict,
+ masks_path: str | pathlib.Path,
+ return_masks: bool = True,
+ input_data_format: ChannelDimension | str = None,
+) -> dict:
+ """
+ Prepare a coco panoptic annotation for DETR.
+ """
+ image_height, image_width = get_image_size(image, channel_dim=input_data_format)
+ annotation_path = pathlib.Path(masks_path) / target["file_name"]
+
+ new_target = {}
+ new_target["image_id"] = np.asarray([target["image_id"] if "image_id" in target else target["id"]], dtype=np.int64)
+ new_target["size"] = np.asarray([image_height, image_width], dtype=np.int64)
+ new_target["orig_size"] = np.asarray([image_height, image_width], dtype=np.int64)
+
+ if "segments_info" in target:
+ masks = np.asarray(PIL.Image.open(annotation_path), dtype=np.uint32)
+ masks = rgb_to_id(masks)
+
+ ids = np.array([segment_info["id"] for segment_info in target["segments_info"]])
+ masks = masks == ids[:, None, None]
+ masks = masks.astype(np.uint8)
+ if return_masks:
+ new_target["masks"] = masks
+ new_target["boxes"] = masks_to_boxes(masks)
+ new_target["class_labels"] = np.array(
+ [segment_info["category_id"] for segment_info in target["segments_info"]], dtype=np.int64
+ )
+ new_target["iscrowd"] = np.asarray(
+ [segment_info["iscrowd"] for segment_info in target["segments_info"]], dtype=np.int64
+ )
+ new_target["area"] = np.asarray(
+ [segment_info["area"] for segment_info in target["segments_info"]], dtype=np.float32
+ )
+
+ return new_target
+
+
+# Adapted from transformers.models.detr.image_processing_detr.DetrImageProcessorKwargs
+class DetrImageProcessorKwargs(ImagesKwargs, total=False):
+ r"""
+ format (`str`, *optional*, defaults to `AnnotationFormat.COCO_DETECTION`):
+ Data format of the annotations. One of "coco_detection" or "coco_panoptic".
+ do_convert_annotations (`bool`, *optional*, defaults to `True`):
+ Controls whether to convert the annotations to the format expected by the DETR model. Converts the
+ bounding boxes to the format `(center_x, center_y, width, height)` and in the range `[0, 1]`.
+ Can be overridden by the `do_convert_annotations` parameter in the `preprocess` method.
+ """
+
+ format: str | AnnotationFormat
+ do_convert_annotations: bool
+
+
+# Adapted from transformers.models.detr.image_processing_detr.binary_mask_to_rle
+def binary_mask_to_rle(mask):
+ """
+ Converts given binary mask of shape `(height, width)` to the run-length encoding (RLE) format.
+
+ Args:
+ mask (`torch.Tensor` or `numpy.array`):
+ A binary mask tensor of shape `(height, width)` where 0 denotes background and 1 denotes the target
+ segment_id or class_id.
+ Returns:
+ `List`: Run-length encoded list of the binary mask. Refer to COCO API for more information about the RLE
+ format.
+ """
+ from ...utils import is_torch_tensor
+
+ if is_torch_tensor(mask):
+ mask = mask.numpy()
+
+ pixels = mask.flatten()
+ pixels = np.concatenate([[0], pixels, [0]])
+ runs = np.where(pixels[1:] != pixels[:-1])[0] + 1
+ runs[1::2] -= runs[::2]
+ return list(runs)
+
+
+# Adapted from transformers.models.detr.image_processing_detr.check_segment_validity
+def check_segment_validity(mask_labels, mask_probs, k, mask_threshold=0.5, overlap_mask_area_threshold=0.8):
+ # Get the mask associated with the k class
+ mask_k = mask_labels == k
+ mask_k_area = mask_k.sum()
+
+ # Compute the area of all the stuff in query k
+ original_area = (mask_probs[k] >= mask_threshold).sum()
+ mask_exists = mask_k_area > 0 and original_area > 0
+
+ # Eliminate disconnected tiny segments
+ if mask_exists:
+ area_ratio = mask_k_area / original_area
+ if not area_ratio.item() > overlap_mask_area_threshold:
+ mask_exists = False
+
+ return mask_exists, mask_k
+
+
+# Adapted from transformers.models.detr.image_processing_detr.compute_segments
+def compute_segments(
+ mask_probs,
+ pred_scores,
+ pred_labels,
+ mask_threshold: float = 0.5,
+ overlap_mask_area_threshold: float = 0.8,
+ label_ids_to_fuse: set[int] | None = None,
+ target_size: tuple[int, int] | None = None,
+):
+ import torch
+ from torch import nn
+
+ height = mask_probs.shape[1] if target_size is None else target_size[0]
+ width = mask_probs.shape[2] if target_size is None else target_size[1]
+
+ segmentation = torch.zeros((height, width), dtype=torch.int32, device=mask_probs.device)
+ segments: list[dict] = []
+
+ if target_size is not None:
+ mask_probs = nn.functional.interpolate(
+ mask_probs.unsqueeze(0), size=target_size, mode="bilinear", align_corners=False
+ )[0]
+
+ current_segment_id = 0
+
+ # Weigh each mask by its prediction score
+ mask_probs *= pred_scores.view(-1, 1, 1)
+ mask_labels = mask_probs.argmax(0) # [height, width]
+
+ # Keep track of instances of each class
+ stuff_memory_list: dict[str, int] = {}
+ for k in range(pred_labels.shape[0]):
+ pred_class = pred_labels[k].item()
+ should_fuse = pred_class in label_ids_to_fuse
+
+ # Check if mask exists and large enough to be a segment
+ mask_exists, mask_k = check_segment_validity(
+ mask_labels, mask_probs, k, mask_threshold, overlap_mask_area_threshold
+ )
+
+ if mask_exists:
+ if pred_class in stuff_memory_list:
+ current_segment_id = stuff_memory_list[pred_class]
+ else:
+ current_segment_id += 1
+
+ # Add current object segment to final segmentation map
+ segmentation[mask_k] = current_segment_id
+ segment_score = round(pred_scores[k].item(), 6)
+ segments.append(
+ {
+ "id": current_segment_id,
+ "label_id": pred_class,
+ "was_fused": should_fuse,
+ "score": segment_score,
+ }
+ )
+ if should_fuse:
+ stuff_memory_list[pred_class] = current_segment_id
+
+ return segmentation, segments
+
+
+# Adapted from transformers.models.detr.image_processing_detr.convert_segmentation_to_rle
+def convert_segmentation_to_rle(segmentation):
+ """
+ Converts given segmentation map of shape `(height, width)` to the run-length encoding (RLE) format.
+
+ Args:
+ segmentation (`torch.Tensor` or `numpy.array`):
+ A segmentation map of shape `(height, width)` where each value denotes a segment or class id.
+ Returns:
+ `list[List]`: A list of lists, where each list is the run-length encoding of a segment / class id.
+ """
+ import torch
+
+ segment_ids = torch.unique(segmentation)
+
+ run_length_encodings = []
+ for idx in segment_ids:
+ mask = torch.where(segmentation == idx, 1, 0)
+ rle = binary_mask_to_rle(mask)
+ run_length_encodings.append(rle)
+
+ return run_length_encodings
+
+
+# Adapted from transformers.models.detr.image_processing_detr.remove_low_and_no_objects
+def remove_low_and_no_objects(masks, scores, labels, object_mask_threshold, num_labels):
+ """
+ Binarize the given masks using `object_mask_threshold`, it returns the associated values of `masks`, `scores` and
+ `labels`.
+
+ Args:
+ masks (`torch.Tensor`):
+ A tensor of shape `(num_queries, height, width)`.
+ scores (`torch.Tensor`):
+ A tensor of shape `(num_queries)`.
+ labels (`torch.Tensor`):
+ A tensor of shape `(num_queries)`.
+ object_mask_threshold (`float`):
+ A number between 0 and 1 used to binarize the masks.
+ Raises:
+ `ValueError`: Raised when the first dimension doesn't match in all input tensors.
+ Returns:
+ `tuple[`torch.Tensor`, `torch.Tensor`, `torch.Tensor`]`: The `masks`, `scores` and `labels` without the region
+ < `object_mask_threshold`.
+ """
+ if not (masks.shape[0] == scores.shape[0] == labels.shape[0]):
+ raise ValueError("mask, scores and labels must have the same shape!")
+
+ to_keep = labels.ne(num_labels) & (scores > object_mask_threshold)
+
+ return masks[to_keep], scores[to_keep], labels[to_keep]
+
+
+@auto_docstring
+class DetrImageProcessorPil(PilBackend):
+ resample = PILImageResampling.BILINEAR
+ image_mean = IMAGENET_DEFAULT_MEAN
+ image_std = IMAGENET_DEFAULT_STD
+ format = AnnotationFormat.COCO_DETECTION
+ do_resize = True
+ do_rescale = True
+ do_normalize = True
+ do_pad = True
+ size = {"shortest_edge": 800, "longest_edge": 1333}
+ default_to_square = False
+ model_input_names = ["pixel_values", "pixel_mask"]
+ valid_kwargs = DetrImageProcessorKwargs
+
+ def __init__(self, **kwargs: Unpack[DetrImageProcessorKwargs]) -> None:
+ kwargs.setdefault("do_pad", kwargs.pop("pad_and_return_pixel_mask", self.do_pad))
+
+ size = kwargs.pop("size", None)
+ max_size = None if size is None else kwargs.pop("max_size", 1333)
+ size = size if size is not None else {"shortest_edge": 800, "longest_edge": 1333}
+ # Convert size dict for backwards compat with max_size parameter
+ if size is not None:
+ from ...image_processing_utils import get_size_dict
+
+ kwargs["size"] = get_size_dict(size, max_size=max_size, default_to_square=False)
+
+ # Backwards compatibility
+ do_convert_annotations = kwargs.get("do_convert_annotations")
+ do_normalize = kwargs.get("do_normalize")
+ if do_convert_annotations is None and getattr(self, "do_convert_annotations", None) is None:
+ self.do_convert_annotations = do_normalize if do_normalize is not None else self.do_normalize
+
+ super().__init__(**kwargs)
+
+ def prepare_annotation(
+ self,
+ image: np.ndarray,
+ target: dict,
+ format: AnnotationFormat | None = None,
+ return_segmentation_masks: bool | None = None,
+ masks_path: str | pathlib.Path | None = None,
+ input_data_format: str | ChannelDimension | None = None,
+ ) -> dict:
+ """
+ Prepare an annotation for feeding into DETR model.
+ """
+ format = format if format is not None else self.format
+
+ if format == AnnotationFormat.COCO_DETECTION:
+ return_segmentation_masks = False if return_segmentation_masks is None else return_segmentation_masks
+ target = prepare_coco_detection_annotation(
+ image, target, return_segmentation_masks, input_data_format=input_data_format
+ )
+ elif format == AnnotationFormat.COCO_PANOPTIC:
+ return_segmentation_masks = True if return_segmentation_masks is None else return_segmentation_masks
+ target = prepare_coco_panoptic_annotation(
+ image,
+ target,
+ masks_path=masks_path,
+ return_masks=return_segmentation_masks,
+ input_data_format=input_data_format,
+ )
+ else:
+ raise ValueError(f"Format {format} is not supported.")
+ return target
+
+ def resize(
+ self,
+ image: np.ndarray,
+ size: SizeDict,
+ resample: Optional["PILImageResampling"] = None,
+ **kwargs,
+ ) -> np.ndarray:
+ """
+ Resize the image to the given size. Size can be `min_size` (scalar) or `(height, width)` tuple. If size is an
+ int, smaller edge of the image will be matched to this number.
+
+ Args:
+ image (`np.ndarray`):
+ Image to resize.
+ size (`SizeDict`):
+ Size of the image's `(height, width)` dimensions after resizing. Available options are:
+ - `{"height": int, "width": int}`: The image will be resized to the exact size `(height, width)`.
+ Do NOT keep the aspect ratio.
+ - `{"shortest_edge": int, "longest_edge": int}`: The image will be resized to a maximum size respecting
+ the aspect ratio and keeping the shortest edge less or equal to `shortest_edge` and the longest edge
+ less or equal to `longest_edge`.
+ - `{"max_height": int, "max_width": int}`: The image will be resized to the maximum size respecting the
+ aspect ratio and keeping the height less or equal to `max_height` and the width less or equal to
+ `max_width`.
+ resample (`PILImageResampling`, *optional*, defaults to `PILImageResampling.BILINEAR`):
+ Resampling filter to use if resizing the image.
+ """
+ resample = resample if resample is not None else self.resample
+
+ if size.shortest_edge and size.longest_edge:
+ # Resize the image so that the shortest edge or the longest edge is of the given size
+ # while maintaining the aspect ratio of the original image.
+ new_size = get_size_with_aspect_ratio(
+ image.shape[-2:],
+ size.shortest_edge,
+ size.longest_edge or size.shortest_edge,
+ )
+ elif size.max_height and size.max_width:
+ new_size = get_image_size_for_max_height_width(image.shape[-2:], size.max_height, size.max_width)
+ elif size.height and size.width:
+ new_size = (size.height, size.width)
+ else:
+ raise ValueError(
+ f"Size must contain 'height' and 'width' keys or 'shortest_edge' and 'longest_edge' keys. Got {size}."
+ )
+
+ image = super().resize(
+ image,
+ size=SizeDict(height=new_size[0], width=new_size[1]),
+ resample=resample,
+ **kwargs,
+ )
+ return image
+
+ def resize_annotation(
+ self,
+ annotation: dict[str, Any],
+ orig_size: tuple[int, int],
+ target_size: tuple[int, int],
+ threshold: float = 0.5,
+ resample: Optional["PILImageResampling"] = PILImageResampling.NEAREST,
+ ):
+ """
+ Resizes an annotation to a target size.
+
+ Args:
+ annotation (`dict[str, Any]`):
+ The annotation dictionary.
+ orig_size (`tuple[int, int]`):
+ The original size of the input image.
+ target_size (`tuple[int, int]`):
+ The target size of the image, as returned by the preprocessing `resize` step.
+ threshold (`float`, *optional*, defaults to 0.5):
+ The threshold used to binarize the segmentation masks.
+ resample (`PILImageResampling`, defaults to `PILImageResampling.NEAREST`):
+ The resampling filter to use when resizing the masks.
+ """
+ ratios = tuple(float(s) / float(s_orig) for s, s_orig in zip(target_size, orig_size))
+ ratio_height, ratio_width = ratios
+
+ new_annotation = {}
+ new_annotation["size"] = target_size
+
+ for key, value in annotation.items():
+ if key == "boxes":
+ boxes = value
+ scaled_boxes = boxes * np.asarray(
+ [ratio_width, ratio_height, ratio_width, ratio_height], dtype=np.float32
+ )
+ new_annotation["boxes"] = scaled_boxes
+ elif key == "area":
+ area = value
+ scaled_area = area * (ratio_width * ratio_height)
+ new_annotation["area"] = scaled_area
+ elif key == "masks":
+ masks = value[:, None]
+ masks = np.array([resize(mask, target_size, resample=resample) for mask in masks])
+ masks = masks.astype(np.float32)
+ masks = masks[:, 0] > threshold
+ new_annotation["masks"] = masks
+ elif key == "size":
+ new_annotation["size"] = target_size
+ else:
+ new_annotation[key] = value
+
+ return new_annotation
+
+ def normalize_annotation(self, annotation: dict, image_size: tuple[int, int]) -> dict:
+ image_height, image_width = image_size
+ norm_annotation = {}
+ for key, value in annotation.items():
+ if key == "boxes":
+ boxes = value
+ boxes = corners_to_center_format(boxes)
+ boxes /= np.asarray([image_width, image_height, image_width, image_height], dtype=np.float32)
+ norm_annotation[key] = boxes
+ else:
+ norm_annotation[key] = value
+ return norm_annotation
+
+ def _update_annotation_for_padded_image(
+ self,
+ annotation: dict,
+ input_image_size: tuple[int, int],
+ output_image_size: tuple[int, int],
+ padding,
+ update_bboxes,
+ ) -> dict:
+ """
+ Update the annotation for a padded image.
+ """
+ new_annotation = {}
+ new_annotation["size"] = output_image_size
+ ratio_height, ratio_width = (input / output for output, input in zip(output_image_size, input_image_size))
+
+ for key, value in annotation.items():
+ if key == "masks":
+ masks = value
+ masks = pad(
+ masks,
+ padding,
+ mode=PaddingMode.CONSTANT,
+ constant_values=0,
+ input_data_format=ChannelDimension.FIRST,
+ )
+ masks = safe_squeeze(masks, 1)
+ new_annotation["masks"] = masks
+ elif key == "boxes" and update_bboxes:
+ boxes = value
+ boxes *= np.asarray(
+ [
+ input_image_size[1] / output_image_size[1],
+ input_image_size[0] / output_image_size[0],
+ input_image_size[1] / output_image_size[1],
+ input_image_size[0] / output_image_size[0],
+ ]
+ )
+ new_annotation["boxes"] = boxes
+ elif key == "size":
+ new_annotation["size"] = output_image_size
+ else:
+ new_annotation[key] = value
+ return new_annotation
+
+ def pad(
+ self,
+ image: np.ndarray,
+ padded_size: tuple[int, int],
+ annotation: dict[str, Any] | None = None,
+ update_bboxes: bool = True,
+ fill: int = 0,
+ ):
+ input_height, input_width = get_image_size(image, channel_dim=ChannelDimension.FIRST)
+ output_height, output_width = padded_size
+ padding_bottom = output_height - input_height
+ padding_right = output_width - input_width
+ if padding_bottom < 0 or padding_right < 0:
+ raise ValueError(
+ f"Padding dimensions are negative. Please make sure that the padded size is larger than the "
+ f"original size. Got padded size: {padded_size}, original size: {(input_height, input_width)}."
+ )
+ if (input_height, input_width) != padded_size:
+ padding = ((0, padding_bottom), (0, padding_right))
+ image = pad(
+ image,
+ padding,
+ mode=PaddingMode.CONSTANT,
+ constant_values=fill,
+ data_format=ChannelDimension.FIRST,
+ input_data_format=ChannelDimension.FIRST,
+ )
+ if annotation is not None:
+ annotation = self._update_annotation_for_padded_image(
+ annotation, (input_height, input_width), (output_height, output_width), padding, update_bboxes
+ )
+
+ # Make a pixel mask for the image, where 1 indicates a valid pixel and 0 indicates padding.
+ pixel_mask = np.zeros(padded_size, dtype=np.int64)
+ pixel_mask[:input_height, :input_width] = 1
+
+ return image, pixel_mask, annotation
+
+ @auto_docstring
+ def preprocess(
+ self,
+ images: ImageInput,
+ annotations: AnnotationType | list[AnnotationType] | None = None,
+ return_segmentation_masks: bool | None = None,
+ masks_path: str | pathlib.Path | None = None,
+ **kwargs: Unpack[DetrImageProcessorKwargs],
+ ) -> BatchFeature:
+ r"""
+ annotations (`AnnotationType` or `list[AnnotationType]`, *optional*):
+ Annotations to transform according to the padding that is applied to the images.
+ return_segmentation_masks (`bool`, *optional*, defaults to `self.return_segmentation_masks`):
+ Whether to return segmentation masks.
+ masks_path (`str` or `pathlib.Path`, *optional*):
+ Path to the directory containing the segmentation masks.
+ """
+ return super().preprocess(images, annotations, return_segmentation_masks, masks_path, **kwargs)
+
+ def _preprocess(
+ self,
+ images: list[np.ndarray],
+ annotations: AnnotationType | list[AnnotationType] | None,
+ return_segmentation_masks: bool,
+ masks_path: str | pathlib.Path | None,
+ do_resize: bool,
+ size: SizeDict,
+ resample: "PILImageResampling | None",
+ do_rescale: bool,
+ rescale_factor: float,
+ do_normalize: bool,
+ do_convert_annotations: bool,
+ image_mean: float | list[float] | None,
+ image_std: float | list[float] | None,
+ do_pad: bool,
+ pad_size: SizeDict | None,
+ format: str | AnnotationFormat | None,
+ return_tensors: str | TensorType | None,
+ **kwargs,
+ ) -> BatchFeature:
+ """
+ Preprocess an image or a batch of images so that it can be used by the model.
+ """
+ if annotations is not None and isinstance(annotations, dict):
+ annotations = [annotations]
+
+ if annotations is not None and len(images) != len(annotations):
+ raise ValueError(
+ f"The number of images ({len(images)}) and annotations ({len(annotations)}) do not match."
+ )
+
+ format = AnnotationFormat(format)
+ if annotations is not None:
+ validate_annotations(format, SUPPORTED_ANNOTATION_FORMATS, annotations)
+
+ if (
+ masks_path is not None
+ and format == AnnotationFormat.COCO_PANOPTIC
+ and not isinstance(masks_path, (pathlib.Path, str))
+ ):
+ raise ValueError(
+ "The path to the directory containing the mask PNG files should be provided as a"
+ f" `pathlib.Path` or string object, but is {type(masks_path)} instead."
+ )
+
+ data = {}
+
+ # Import torch if needed for tensor conversion
+ if return_tensors == "pt":
+ if not is_torch_available():
+ raise ImportError("PyTorch is required for tensor conversion.")
+
+ processed_images = []
+ processed_annotations = []
+ pixel_masks = [] # Initialize pixel_masks here
+ for image, annotation in zip(images, annotations if annotations is not None else [None] * len(images)):
+ # prepare (COCO annotations as a list of Dict -> DETR target as a single Dict per image)
+ if annotations is not None:
+ annotation = self.prepare_annotation(
+ image,
+ annotation,
+ format,
+ return_segmentation_masks=return_segmentation_masks,
+ masks_path=masks_path,
+ input_data_format=ChannelDimension.FIRST,
+ )
+
+ if do_resize:
+ resized_image = self.resize(image, size=size, resample=resample)
+ if annotations is not None:
+ annotation = self.resize_annotation(
+ annotation,
+ orig_size=get_image_size(image, channel_dim=ChannelDimension.FIRST),
+ target_size=get_image_size(resized_image, channel_dim=ChannelDimension.FIRST),
+ )
+ image = resized_image
+
+ if do_rescale:
+ image = self.rescale(image, rescale_factor)
+ if do_normalize:
+ image = self.normalize(image, image_mean, image_std)
+
+ if do_convert_annotations and annotations is not None:
+ annotation = self.normalize_annotation(annotation, get_image_size(image, ChannelDimension.FIRST))
+
+ processed_images.append(image)
+ processed_annotations.append(annotation)
+ images = processed_images
+ annotations = processed_annotations if annotations is not None else None
+
+ if do_pad:
+ # depends on all resized image shapes so we need another loop
+ if pad_size is not None:
+ padded_size = (pad_size.height, pad_size.width)
+ else:
+ padded_size = get_max_height_width(images, input_data_format=ChannelDimension.FIRST)
+
+ padded_images = []
+ padded_annotations = []
+ for image, annotation in zip(images, annotations if annotations is not None else [None] * len(images)):
+ # Pads images and returns their mask: {'pixel_values': ..., 'pixel_mask': ...}
+ image_height, image_width = get_image_size(image, channel_dim=ChannelDimension.FIRST)
+ if padded_size == (image_height, image_width):
+ padded_images.append(image)
+ pixel_masks.append(np.ones(padded_size, dtype=np.int64))
+ padded_annotations.append(annotation)
+ continue
+ image, pixel_mask, annotation = self.pad(
+ image, padded_size, annotation=annotation, update_bboxes=do_convert_annotations
+ )
+ padded_images.append(image)
+ padded_annotations.append(annotation)
+ pixel_masks.append(pixel_mask)
+ images = padded_images
+ annotations = padded_annotations if annotations is not None else None
+ data.update({"pixel_mask": pixel_masks})
+
+ data.update({"pixel_values": images})
+ encoded_inputs = BatchFeature(data, tensor_type=return_tensors)
+ if annotations is not None:
+ encoded_inputs["labels"] = [
+ BatchFeature(annotation, tensor_type=return_tensors) for annotation in annotations
+ ]
+ return encoded_inputs
+
+ @requires(backends=("torch",))
+ def post_process_object_detection(
+ self, outputs, threshold: float = 0.5, target_sizes: TensorType | list[tuple] | None = None
+ ):
+ """
+ Converts the raw output of [`DetrForObjectDetection`] into final bounding boxes in (top_left_x, top_left_y,
+ bottom_right_x, bottom_right_y) format. Only supports PyTorch.
+
+ Args:
+ outputs ([`DetrObjectDetectionOutput`]):
+ Raw outputs of the model.
+ threshold (`float`, *optional*):
+ Score threshold to keep object detection predictions.
+ target_sizes (`torch.Tensor` or `list[tuple[int, int]]`, *optional*):
+ Tensor of shape `(batch_size, 2)` or list of tuples (`tuple[int, int]`) containing the target size
+ `(height, width)` of each image in the batch. If unset, predictions will not be resized.
+ Returns:
+ `list[Dict]`: A list of dictionaries, each dictionary containing the scores, labels and boxes for an image
+ in the batch as predicted by the model.
+ """
+ if not is_torch_available():
+ raise ImportError("PyTorch is required for post-processing.")
+ import torch
+ from torch import nn
+
+ out_logits, out_bbox = outputs.logits, outputs.pred_boxes
+
+ if target_sizes is not None:
+ if len(out_logits) != len(target_sizes):
+ raise ValueError(
+ "Make sure that you pass in as many target sizes as the batch dimension of the logits"
+ )
+
+ prob = nn.functional.softmax(out_logits, -1)
+ scores, labels = prob[..., :-1].max(-1)
+
+ # Convert to [x0, y0, x1, y1] format
+ boxes = center_to_corners_format(out_bbox)
+
+ # Convert from relative [0, 1] to absolute [0, height] coordinates
+ if target_sizes is not None:
+ if isinstance(target_sizes, list):
+ img_h = torch.Tensor([i[0] for i in target_sizes])
+ img_w = torch.Tensor([i[1] for i in target_sizes])
+ else:
+ img_h, img_w = target_sizes.unbind(1)
+
+ scale_fct = torch.stack([img_w, img_h, img_w, img_h], dim=1).to(boxes.device)
+ boxes = boxes * scale_fct[:, None, :]
+
+ results = []
+ for s, l, b in zip(scores, labels, boxes):
+ score = s[s > threshold]
+ label = l[s > threshold]
+ box = b[s > threshold]
+ results.append({"scores": score, "labels": label, "boxes": box})
+
+ return results
+
+ @requires(backends=("torch",))
+ def post_process_semantic_segmentation(self, outputs, target_sizes: list[tuple[int, int]] | None = None):
+ """
+ Converts the output of [`DetrForSegmentation`] into semantic segmentation maps. Only supports PyTorch.
+
+ Args:
+ outputs ([`DetrForSegmentation`]):
+ Raw outputs of the model.
+ target_sizes (`list[tuple[int, int]]`, *optional*):
+ A list of tuples (`tuple[int, int]`) containing the target size (height, width) of each image in the
+ batch. If unset, predictions will not be resized.
+ Returns:
+ `list[torch.Tensor]`:
+ A list of length `batch_size`, where each item is a semantic segmentation map of shape (height, width)
+ corresponding to the target_sizes entry (if `target_sizes` is specified). Each entry of each
+ `torch.Tensor` correspond to a semantic class id.
+ """
+ if not is_torch_available():
+ raise ImportError("PyTorch is required for post-processing.")
+ import torch
+ from torch import nn
+
+ class_queries_logits = outputs.logits # [batch_size, num_queries, num_classes+1]
+ masks_queries_logits = outputs.pred_masks # [batch_size, num_queries, height, width]
+
+ # Remove the null class `[..., :-1]`
+ masks_classes = class_queries_logits.softmax(dim=-1)[..., :-1]
+ masks_probs = masks_queries_logits.sigmoid() # [batch_size, num_queries, height, width]
+
+ # Semantic segmentation logits of shape (batch_size, num_classes, height, width)
+ segmentation = torch.einsum("bqc, bqhw -> bchw", masks_classes, masks_probs)
+ batch_size = class_queries_logits.shape[0]
+
+ # Resize logits and compute semantic segmentation maps
+ if target_sizes is not None:
+ if batch_size != len(target_sizes):
+ raise ValueError(
+ "Make sure that you pass in as many target sizes as the batch dimension of the logits"
+ )
+
+ semantic_segmentation = []
+ for idx in range(batch_size):
+ resized_logits = nn.functional.interpolate(
+ segmentation[idx].unsqueeze(dim=0), size=target_sizes[idx], mode="bilinear", align_corners=False
+ )
+ semantic_map = resized_logits[0].argmax(dim=0)
+ semantic_segmentation.append(semantic_map)
+ else:
+ semantic_segmentation = segmentation.argmax(dim=1)
+ semantic_segmentation = [semantic_segmentation[i] for i in range(semantic_segmentation.shape[0])]
+
+ return semantic_segmentation
+
+ @requires(backends=("torch",))
+ def post_process_instance_segmentation(
+ self,
+ outputs,
+ threshold: float = 0.5,
+ mask_threshold: float = 0.5,
+ overlap_mask_area_threshold: float = 0.8,
+ target_sizes: list[tuple[int, int]] | None = None,
+ return_coco_annotation: bool | None = False,
+ ) -> list[dict]:
+ """
+ Converts the output of [`DetrForSegmentation`] into instance segmentation predictions. Only supports PyTorch.
+
+ Args:
+ outputs ([`DetrForSegmentation`]):
+ Raw outputs of the model.
+ threshold (`float`, *optional*, defaults to 0.5):
+ The probability score threshold to keep predicted instance masks.
+ mask_threshold (`float`, *optional*, defaults to 0.5):
+ Threshold to use when turning the predicted masks into binary values.
+ overlap_mask_area_threshold (`float`, *optional*, defaults to 0.8):
+ The overlap mask area threshold to merge or discard small disconnected parts within each binary
+ instance mask.
+ target_sizes (`list[Tuple]`, *optional*):
+ List of length (batch_size), where each list item (`tuple[int, int]]`) corresponds to the requested
+ final size (height, width) of each prediction. If unset, predictions will not be resized.
+ return_coco_annotation (`bool`, *optional*):
+ Defaults to `False`. If set to `True`, segmentation maps are returned in COCO run-length encoding (RLE)
+ format.
+ Returns:
+ `list[Dict]`: A list of dictionaries, one per image, each dictionary containing two keys:
+ - **segmentation** -- A tensor of shape `(height, width)` where each pixel represents a `segment_id` or
+ `list[List]` run-length encoding (RLE) of the segmentation map if return_coco_annotation is set to
+ `True`. Set to `None` if no mask if found above `threshold`.
+ - **segments_info** -- A dictionary that contains additional information on each segment.
+ - **id** -- An integer representing the `segment_id`.
+ - **label_id** -- An integer representing the label / semantic class id corresponding to `segment_id`.
+ - **score** -- Prediction score of segment with `segment_id`.
+ """
+ if not is_torch_available():
+ raise ImportError("PyTorch is required for post-processing.")
+ import torch
+ from torch import nn
+
+ class_queries_logits = outputs.logits # [batch_size, num_queries, num_classes+1]
+ masks_queries_logits = outputs.pred_masks # [batch_size, num_queries, height, width]
+
+ batch_size = class_queries_logits.shape[0]
+ num_labels = class_queries_logits.shape[-1] - 1
+
+ mask_probs = masks_queries_logits.sigmoid() # [batch_size, num_queries, height, width]
+
+ # Predicted label and score of each query (batch_size, num_queries)
+ pred_scores, pred_labels = nn.functional.softmax(class_queries_logits, dim=-1).max(-1)
+
+ # Loop over items in batch size
+ results: list[dict[str, TensorType]] = []
+
+ for i in range(batch_size):
+ mask_probs_item, pred_scores_item, pred_labels_item = remove_low_and_no_objects(
+ mask_probs[i], pred_scores[i], pred_labels[i], threshold, num_labels
+ )
+
+ # No mask found
+ if mask_probs_item.shape[0] <= 0:
+ height, width = target_sizes[i] if target_sizes is not None else mask_probs_item.shape[1:]
+ segmentation = torch.zeros((height, width)) - 1
+ results.append({"segmentation": segmentation, "segments_info": []})
+ continue
+
+ # Get segmentation map and segment information of batch item
+ target_size = target_sizes[i] if target_sizes is not None else None
+ segmentation, segments = compute_segments(
+ mask_probs=mask_probs_item,
+ pred_scores=pred_scores_item,
+ pred_labels=pred_labels_item,
+ mask_threshold=mask_threshold,
+ overlap_mask_area_threshold=overlap_mask_area_threshold,
+ label_ids_to_fuse=[],
+ target_size=target_size,
+ )
+
+ # Return segmentation map in run-length encoding (RLE) format
+ if return_coco_annotation:
+ segmentation = convert_segmentation_to_rle(segmentation)
+
+ results.append({"segmentation": segmentation, "segments_info": segments})
+ return results
+
+ @requires(backends=("torch",))
+ def post_process_panoptic_segmentation(
+ self,
+ outputs,
+ threshold: float = 0.5,
+ mask_threshold: float = 0.5,
+ overlap_mask_area_threshold: float = 0.8,
+ label_ids_to_fuse: set[int] | None = None,
+ target_sizes: list[tuple[int, int]] | None = None,
+ ) -> list[dict]:
+ """
+ Converts the output of [`DetrForSegmentation`] into image panoptic segmentation predictions. Only supports
+ PyTorch.
+
+ Args:
+ outputs ([`DetrForSegmentation`]):
+ The outputs from [`DetrForSegmentation`].
+ threshold (`float`, *optional*, defaults to 0.5):
+ The probability score threshold to keep predicted instance masks.
+ mask_threshold (`float`, *optional*, defaults to 0.5):
+ Threshold to use when turning the predicted masks into binary values.
+ overlap_mask_area_threshold (`float`, *optional*, defaults to 0.8):
+ The overlap mask area threshold to merge or discard small disconnected parts within each binary
+ instance mask.
+ label_ids_to_fuse (`Set[int]`, *optional*):
+ The labels in this state will have all their instances be fused together. For instance we could say
+ there can only be one sky in an image, but several persons, so the label ID for sky would be in that
+ set, but not the one for person.
+ target_sizes (`list[Tuple]`, *optional*):
+ List of length (batch_size), where each list item (`tuple[int, int]]`) corresponds to the requested
+ final size (height, width) of each prediction in batch. If unset, predictions will not be resized.
+ Returns:
+ `list[Dict]`: A list of dictionaries, one per image, each dictionary containing two keys:
+ - **segmentation** -- a tensor of shape `(height, width)` where each pixel represents a `segment_id` or
+ `None` if no mask if found above `threshold`. If `target_sizes` is specified, segmentation is resized to
+ the corresponding `target_sizes` entry.
+ - **segments_info** -- A dictionary that contains additional information on each segment.
+ - **id** -- an integer representing the `segment_id`.
+ - **label_id** -- An integer representing the label / semantic class id corresponding to `segment_id`.
+ - **was_fused** -- a boolean, `True` if `label_id` was in `label_ids_to_fuse`, `False` otherwise.
+ Multiple instances of the same class / label were fused and assigned a single `segment_id`.
+ - **score** -- Prediction score of segment with `segment_id`.
+ """
+
+ if label_ids_to_fuse is None:
+ logger.warning_once("`label_ids_to_fuse` unset. No instance will be fused.")
+ label_ids_to_fuse = set()
+
+ if not is_torch_available():
+ raise ImportError("PyTorch is required for post-processing.")
+ import torch
+ from torch import nn
+
+ class_queries_logits = outputs.logits # [batch_size, num_queries, num_classes+1]
+ masks_queries_logits = outputs.pred_masks # [batch_size, num_queries, height, width]
+
+ batch_size = class_queries_logits.shape[0]
+ num_labels = class_queries_logits.shape[-1] - 1
+
+ mask_probs = masks_queries_logits.sigmoid() # [batch_size, num_queries, height, width]
+
+ # Predicted label and score of each query (batch_size, num_queries)
+ pred_scores, pred_labels = nn.functional.softmax(class_queries_logits, dim=-1).max(-1)
+
+ # Loop over items in batch size
+ results: list[dict[str, TensorType]] = []
+
+ for i in range(batch_size):
+ mask_probs_item, pred_scores_item, pred_labels_item = remove_low_and_no_objects(
+ mask_probs[i], pred_scores[i], pred_labels[i], threshold, num_labels
+ )
+
+ # No mask found
+ if mask_probs_item.shape[0] <= 0:
+ height, width = target_sizes[i] if target_sizes is not None else mask_probs_item.shape[1:]
+ segmentation = torch.zeros((height, width)) - 1
+ results.append({"segmentation": segmentation, "segments_info": []})
+ continue
+
+ # Get segmentation map and segment information of batch item
+ target_size = target_sizes[i] if target_sizes is not None else None
+ segmentation, segments = compute_segments(
+ mask_probs=mask_probs_item,
+ pred_scores=pred_scores_item,
+ pred_labels=pred_labels_item,
+ mask_threshold=mask_threshold,
+ overlap_mask_area_threshold=overlap_mask_area_threshold,
+ label_ids_to_fuse=label_ids_to_fuse,
+ target_size=target_size,
+ )
+
+ results.append({"segmentation": segmentation, "segments_info": segments})
+ return results
+
+
+__all__ = ["DetrImageProcessorPil"]
diff --git a/third_party/transformers/src/transformers/models/detr/modeling_detr.py b/third_party/transformers/src/transformers/models/detr/modeling_detr.py
new file mode 100644
index 0000000000000000000000000000000000000000..384cc388cfd77a5e4e6c82d90d354bc132346321
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/detr/modeling_detr.py
@@ -0,0 +1,1639 @@
+# Copyright 2021 Facebook AI Research The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""PyTorch DETR model."""
+
+import math
+from collections.abc import Callable
+from dataclasses import dataclass
+
+import torch
+import torch.nn as nn
+
+from ... import initialization as init
+from ...activations import ACT2FN
+from ...backbone_utils import load_backbone
+from ...masking_utils import create_bidirectional_mask
+from ...modeling_layers import GradientCheckpointingLayer
+from ...modeling_outputs import (
+ BaseModelOutput,
+ BaseModelOutputWithCrossAttentions,
+ Seq2SeqModelOutput,
+)
+from ...modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
+from ...processing_utils import Unpack
+from ...pytorch_utils import compile_compatible_method_lru_cache
+from ...utils import (
+ ModelOutput,
+ TransformersKwargs,
+ auto_docstring,
+ logging,
+)
+from ...utils.generic import can_return_tuple, merge_with_config_defaults
+from ...utils.output_capturing import capture_outputs
+from .configuration_detr import DetrConfig
+
+
+logger = logging.get_logger(__name__)
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ Base class for outputs of the DETR decoder. This class adds one attribute to BaseModelOutputWithCrossAttentions,
+ namely an optional stack of intermediate decoder activations, i.e. the output of each decoder layer, each of them
+ gone through a layernorm. This is useful when training the model with auxiliary decoding losses.
+ """
+)
+class DetrDecoderOutput(BaseModelOutputWithCrossAttentions):
+ r"""
+ cross_attentions (`tuple(torch.FloatTensor)`, *optional*, returned when `output_attentions=True` and `config.add_cross_attention=True` is passed or when `config.output_attentions=True`):
+ Tuple of `torch.FloatTensor` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
+ sequence_length)`. Attentions weights of the decoder's cross-attention layer, after the attention softmax,
+ used to compute the weighted average in the cross-attention heads.
+ intermediate_hidden_states (`torch.FloatTensor` of shape `(config.decoder_layers, batch_size, num_queries, hidden_size)`, *optional*, returned when `config.auxiliary_loss=True`):
+ Intermediate decoder activations, i.e. the output of each decoder layer, each of them gone through a
+ layernorm.
+ """
+
+ intermediate_hidden_states: torch.FloatTensor | None = None
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ Base class for outputs of the DETR encoder-decoder model. This class adds one attribute to Seq2SeqModelOutput,
+ namely an optional stack of intermediate decoder activations, i.e. the output of each decoder layer, each of them
+ gone through a layernorm. This is useful when training the model with auxiliary decoding losses.
+ """
+)
+class DetrModelOutput(Seq2SeqModelOutput):
+ r"""
+ last_hidden_state (`torch.FloatTensor` of shape `(batch_size, sequence_length, hidden_size)`):
+ Sequence of hidden-states at the output of the last layer of the decoder of the model.
+ intermediate_hidden_states (`torch.FloatTensor` of shape `(config.decoder_layers, batch_size, sequence_length, hidden_size)`, *optional*, returned when `config.auxiliary_loss=True`):
+ Intermediate decoder activations, i.e. the output of each decoder layer, each of them gone through a
+ layernorm.
+ """
+
+ intermediate_hidden_states: torch.FloatTensor | None = None
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ Output type of [`DetrForObjectDetection`].
+ """
+)
+class DetrObjectDetectionOutput(ModelOutput):
+ r"""
+ loss (`torch.FloatTensor` of shape `(1,)`, *optional*, returned when `labels` are provided)):
+ Total loss as a linear combination of a negative log-likehood (cross-entropy) for class prediction and a
+ bounding box loss. The latter is defined as a linear combination of the L1 loss and the generalized
+ scale-invariant IoU loss.
+ loss_dict (`Dict`, *optional*):
+ A dictionary containing the individual losses. Useful for logging.
+ logits (`torch.FloatTensor` of shape `(batch_size, num_queries, num_classes + 1)`):
+ Classification logits (including no-object) for all queries.
+ pred_boxes (`torch.FloatTensor` of shape `(batch_size, num_queries, 4)`):
+ Normalized boxes coordinates for all queries, represented as (center_x, center_y, width, height). These
+ values are normalized in [0, 1], relative to the size of each individual image in the batch (disregarding
+ possible padding). You can use [`~DetrImageProcessor.post_process_object_detection`] to retrieve the
+ unnormalized bounding boxes.
+ auxiliary_outputs (`list[Dict]`, *optional*):
+ Optional, only returned when auxiliary losses are activated (i.e. `config.auxiliary_loss` is set to `True`)
+ and labels are provided. It is a list of dictionaries containing the two above keys (`logits` and
+ `pred_boxes`) for each decoder layer.
+ last_hidden_state (`torch.FloatTensor` of shape `(batch_size, sequence_length, hidden_size)`, *optional*):
+ Sequence of hidden-states at the output of the last layer of the decoder of the model.
+ """
+
+ loss: torch.FloatTensor | None = None
+ loss_dict: dict | None = None
+ logits: torch.FloatTensor | None = None
+ pred_boxes: torch.FloatTensor | None = None
+ auxiliary_outputs: list[dict] | None = None
+ last_hidden_state: torch.FloatTensor | None = None
+ decoder_hidden_states: tuple[torch.FloatTensor] | None = None
+ decoder_attentions: tuple[torch.FloatTensor] | None = None
+ cross_attentions: tuple[torch.FloatTensor] | None = None
+ encoder_last_hidden_state: torch.FloatTensor | None = None
+ encoder_hidden_states: tuple[torch.FloatTensor] | None = None
+ encoder_attentions: tuple[torch.FloatTensor] | None = None
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ Output type of [`DetrForSegmentation`].
+ """
+)
+class DetrSegmentationOutput(ModelOutput):
+ r"""
+ loss (`torch.FloatTensor` of shape `(1,)`, *optional*, returned when `labels` are provided)):
+ Total loss as a linear combination of a negative log-likehood (cross-entropy) for class prediction and a
+ bounding box loss. The latter is defined as a linear combination of the L1 loss and the generalized
+ scale-invariant IoU loss.
+ loss_dict (`Dict`, *optional*):
+ A dictionary containing the individual losses. Useful for logging.
+ logits (`torch.FloatTensor` of shape `(batch_size, num_queries, num_classes + 1)`):
+ Classification logits (including no-object) for all queries.
+ pred_boxes (`torch.FloatTensor` of shape `(batch_size, num_queries, 4)`):
+ Normalized boxes coordinates for all queries, represented as (center_x, center_y, width, height). These
+ values are normalized in [0, 1], relative to the size of each individual image in the batch (disregarding
+ possible padding). You can use [`~DetrImageProcessor.post_process_object_detection`] to retrieve the
+ unnormalized bounding boxes.
+ pred_masks (`torch.FloatTensor` of shape `(batch_size, num_queries, height/4, width/4)`):
+ Segmentation masks logits for all queries. See also
+ [`~DetrImageProcessor.post_process_semantic_segmentation`] or
+ [`~DetrImageProcessor.post_process_instance_segmentation`]
+ [`~DetrImageProcessor.post_process_panoptic_segmentation`] to evaluate semantic, instance and panoptic
+ segmentation masks respectively.
+ auxiliary_outputs (`list[Dict]`, *optional*):
+ Optional, only returned when auxiliary losses are activated (i.e. `config.auxiliary_loss` is set to `True`)
+ and labels are provided. It is a list of dictionaries containing the two above keys (`logits` and
+ `pred_boxes`) for each decoder layer.
+ last_hidden_state (`torch.FloatTensor` of shape `(batch_size, sequence_length, hidden_size)`, *optional*):
+ Sequence of hidden-states at the output of the last layer of the decoder of the model.
+ """
+
+ loss: torch.FloatTensor | None = None
+ loss_dict: dict | None = None
+ logits: torch.FloatTensor | None = None
+ pred_boxes: torch.FloatTensor | None = None
+ pred_masks: torch.FloatTensor | None = None
+ auxiliary_outputs: list[dict] | None = None
+ last_hidden_state: torch.FloatTensor | None = None
+ decoder_hidden_states: tuple[torch.FloatTensor] | None = None
+ decoder_attentions: tuple[torch.FloatTensor] | None = None
+ cross_attentions: tuple[torch.FloatTensor] | None = None
+ encoder_last_hidden_state: torch.FloatTensor | None = None
+ encoder_hidden_states: tuple[torch.FloatTensor] | None = None
+ encoder_attentions: tuple[torch.FloatTensor] | None = None
+
+
+class DetrFrozenBatchNorm2d(nn.Module):
+ """
+ BatchNorm2d where the batch statistics and the affine parameters are fixed.
+
+ Copy-paste from torchvision.misc.ops with added eps before rqsrt, without which any other models than
+ torchvision.models.resnet[18,34,50,101] produce nans.
+ """
+
+ def __init__(self, n):
+ super().__init__()
+ self.register_buffer("weight", torch.ones(n))
+ self.register_buffer("bias", torch.zeros(n))
+ self.register_buffer("running_mean", torch.zeros(n))
+ self.register_buffer("running_var", torch.ones(n))
+
+ def _load_from_state_dict(
+ self, state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs
+ ):
+ num_batches_tracked_key = prefix + "num_batches_tracked"
+ if num_batches_tracked_key in state_dict:
+ del state_dict[num_batches_tracked_key]
+
+ super()._load_from_state_dict(
+ state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs
+ )
+
+ def forward(self, x):
+ # move reshapes to the beginning
+ # to make it user-friendly
+ weight = self.weight.reshape(1, -1, 1, 1)
+ bias = self.bias.reshape(1, -1, 1, 1)
+ running_var = self.running_var.reshape(1, -1, 1, 1)
+ running_mean = self.running_mean.reshape(1, -1, 1, 1)
+ epsilon = 1e-5
+ scale = weight * (running_var + epsilon).rsqrt()
+ bias = bias - running_mean * scale
+ return x * scale + bias
+
+
+def replace_batch_norm(model):
+ r"""
+ Recursively replace all `torch.nn.BatchNorm2d` with `DetrFrozenBatchNorm2d`.
+
+ Args:
+ model (torch.nn.Module):
+ input model
+ """
+ for name, module in model.named_children():
+ if isinstance(module, nn.BatchNorm2d):
+ new_module = DetrFrozenBatchNorm2d(module.num_features)
+
+ if module.weight.device != torch.device("meta"):
+ new_module.weight.copy_(module.weight)
+ new_module.bias.copy_(module.bias)
+ new_module.running_mean.copy_(module.running_mean)
+ new_module.running_var.copy_(module.running_var)
+
+ model._modules[name] = new_module
+
+ if len(list(module.children())) > 0:
+ replace_batch_norm(module)
+
+
+class DetrConvEncoder(nn.Module):
+ """
+ Convolutional backbone, using either the AutoBackbone API or one from the timm library.
+
+ nn.BatchNorm2d layers are replaced by DetrFrozenBatchNorm2d as defined above.
+
+ """
+
+ def __init__(self, config):
+ super().__init__()
+
+ self.config = config
+
+ backbone = load_backbone(config)
+ self.intermediate_channel_sizes = backbone.channels
+
+ # replace batch norm by frozen batch norm
+ with torch.no_grad():
+ replace_batch_norm(backbone)
+
+ # We used to load with timm library directly instead of the AutoBackbone API
+ # so we need to unwrap the `backbone._backbone` module to load weights without mismatch
+ is_timm_model = False
+ if hasattr(backbone, "_backbone"):
+ backbone = backbone._backbone
+ is_timm_model = True
+ self.model = backbone
+
+ backbone_model_type = config.backbone_config.model_type
+ if "resnet" in backbone_model_type:
+ for name, parameter in self.model.named_parameters():
+ if is_timm_model:
+ if "layer2" not in name and "layer3" not in name and "layer4" not in name:
+ parameter.requires_grad_(False)
+ else:
+ if "stage.1" not in name and "stage.2" not in name and "stage.3" not in name:
+ parameter.requires_grad_(False)
+
+ def forward(self, pixel_values: torch.Tensor, pixel_mask: torch.Tensor):
+ # send pixel_values through the model to get list of feature maps
+ features = self.model(pixel_values)
+ if isinstance(features, dict):
+ features = features.feature_maps
+
+ out = []
+ for feature_map in features:
+ # downsample pixel_mask to match shape of corresponding feature_map
+ mask = nn.functional.interpolate(pixel_mask[None].float(), size=feature_map.shape[-2:]).to(torch.bool)[0]
+ out.append((feature_map, mask))
+ return out
+
+
+class DetrSinePositionEmbedding(nn.Module):
+ """
+ This is a more standard version of the position embedding, very similar to the one used by the Attention is all you
+ need paper, generalized to work on images.
+ """
+
+ def __init__(
+ self,
+ num_position_features: int = 64,
+ temperature: int = 10000,
+ normalize: bool = False,
+ scale: float | None = None,
+ ):
+ super().__init__()
+ if scale is not None and normalize is False:
+ raise ValueError("normalize should be True if scale is passed")
+ self.num_position_features = num_position_features
+ self.temperature = temperature
+ self.normalize = normalize
+ self.scale = 2 * math.pi if scale is None else scale
+
+ @compile_compatible_method_lru_cache(maxsize=1)
+ def forward(
+ self,
+ shape: torch.Size,
+ device: torch.device | str,
+ dtype: torch.dtype,
+ mask: torch.Tensor | None = None,
+ ) -> torch.Tensor:
+ if mask is None:
+ mask = torch.zeros((shape[0], shape[2], shape[3]), device=device, dtype=torch.bool)
+ y_embed = mask.cumsum(1, dtype=dtype)
+ x_embed = mask.cumsum(2, dtype=dtype)
+ if self.normalize:
+ eps = 1e-6
+ y_embed = y_embed / (y_embed[:, -1:, :] + eps) * self.scale
+ x_embed = x_embed / (x_embed[:, :, -1:] + eps) * self.scale
+
+ dim_t = torch.arange(self.num_position_features, dtype=torch.int64, device=device).to(dtype)
+ dim_t = self.temperature ** (2 * torch.div(dim_t, 2, rounding_mode="floor") / self.num_position_features)
+
+ pos_x = x_embed[:, :, :, None] / dim_t
+ pos_y = y_embed[:, :, :, None] / dim_t
+ pos_x = torch.stack((pos_x[:, :, :, 0::2].sin(), pos_x[:, :, :, 1::2].cos()), dim=4).flatten(3)
+ pos_y = torch.stack((pos_y[:, :, :, 0::2].sin(), pos_y[:, :, :, 1::2].cos()), dim=4).flatten(3)
+ pos = torch.cat((pos_y, pos_x), dim=3).permute(0, 3, 1, 2)
+ # Flatten spatial dimensions and permute to (batch_size, sequence_length, hidden_size) format
+ # expected by the encoder
+ pos = pos.flatten(2).permute(0, 2, 1)
+ return pos
+
+
+class DetrLearnedPositionEmbedding(nn.Module):
+ """
+ This module learns positional embeddings up to a fixed maximum size.
+ """
+
+ def __init__(self, embedding_dim=256):
+ super().__init__()
+ self.row_embeddings = nn.Embedding(50, embedding_dim)
+ self.column_embeddings = nn.Embedding(50, embedding_dim)
+
+ @compile_compatible_method_lru_cache(maxsize=1)
+ def forward(
+ self,
+ shape: torch.Size,
+ device: torch.device | str,
+ dtype: torch.dtype,
+ mask: torch.Tensor | None = None,
+ ):
+ height, width = shape[-2:]
+ width_values = torch.arange(width, device=device)
+ height_values = torch.arange(height, device=device)
+ x_emb = self.column_embeddings(width_values)
+ y_emb = self.row_embeddings(height_values)
+ pos = torch.cat([x_emb.unsqueeze(0).repeat(height, 1, 1), y_emb.unsqueeze(1).repeat(1, width, 1)], dim=-1)
+ pos = pos.permute(2, 0, 1)
+ pos = pos.unsqueeze(0)
+ pos = pos.repeat(shape[0], 1, 1, 1)
+ # Flatten spatial dimensions and permute to (batch_size, sequence_length, hidden_size) format
+ # expected by the encoder
+ pos = pos.flatten(2).permute(0, 2, 1)
+ return pos
+
+
+# Copied from transformers.models.bert.modeling_bert.eager_attention_forward
+def eager_attention_forward(
+ module: nn.Module,
+ query: torch.Tensor,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ attention_mask: torch.Tensor | None,
+ scaling: float | None = None,
+ dropout: float = 0.0,
+ **kwargs: Unpack[TransformersKwargs],
+):
+ if scaling is None:
+ scaling = query.size(-1) ** -0.5
+
+ # Take the dot product between "query" and "key" to get the raw attention scores.
+ attn_weights = torch.matmul(query, key.transpose(2, 3)) * scaling
+
+ if attention_mask is not None:
+ attn_weights = attn_weights + attention_mask
+
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1)
+ attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
+
+ attn_output = torch.matmul(attn_weights, value)
+ attn_output = attn_output.transpose(1, 2).contiguous()
+
+ return attn_output, attn_weights
+
+
+class DetrSelfAttention(nn.Module):
+ """
+ Multi-headed self-attention from 'Attention Is All You Need' paper.
+
+ In DETR, position embeddings are added to both queries and keys (but not values) in self-attention.
+ """
+
+ def __init__(
+ self,
+ config: DetrConfig,
+ hidden_size: int,
+ num_attention_heads: int,
+ dropout: float = 0.0,
+ bias: bool = True,
+ ):
+ super().__init__()
+ self.config = config
+ self.head_dim = hidden_size // num_attention_heads
+ self.scaling = self.head_dim**-0.5
+ self.attention_dropout = dropout
+ self.is_causal = False
+
+ self.k_proj = nn.Linear(hidden_size, hidden_size, bias=bias)
+ self.v_proj = nn.Linear(hidden_size, hidden_size, bias=bias)
+ self.q_proj = nn.Linear(hidden_size, hidden_size, bias=bias)
+ self.o_proj = nn.Linear(hidden_size, hidden_size, bias=bias)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ attention_mask: torch.Tensor | None = None,
+ position_embeddings: torch.Tensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple[torch.Tensor, torch.Tensor]:
+ """
+ Position embeddings are added to both queries and keys (but not values).
+ """
+ input_shape = hidden_states.shape[:-1]
+ hidden_shape = (*input_shape, -1, self.head_dim)
+
+ query_key_input = hidden_states + position_embeddings if position_embeddings is not None else hidden_states
+
+ query_states = self.q_proj(query_key_input).view(hidden_shape).transpose(1, 2)
+ key_states = self.k_proj(query_key_input).view(hidden_shape).transpose(1, 2)
+ value_states = self.v_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ query_states,
+ key_states,
+ value_states,
+ attention_mask,
+ dropout=0.0 if not self.training else self.attention_dropout,
+ scaling=self.scaling,
+ **kwargs,
+ )
+
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
+ attn_output = self.o_proj(attn_output)
+ return attn_output, attn_weights
+
+
+class DetrCrossAttention(nn.Module):
+ """
+ Multi-headed cross-attention from 'Attention Is All You Need' paper.
+
+ In DETR, queries get their own position embeddings, while keys get encoder position embeddings.
+ Values don't get any position embeddings.
+ """
+
+ def __init__(
+ self,
+ config: DetrConfig,
+ hidden_size: int,
+ num_attention_heads: int,
+ dropout: float = 0.0,
+ bias: bool = True,
+ ):
+ super().__init__()
+ self.config = config
+ self.head_dim = hidden_size // num_attention_heads
+ self.scaling = self.head_dim**-0.5
+ self.attention_dropout = dropout
+ self.is_causal = False
+
+ self.k_proj = nn.Linear(hidden_size, hidden_size, bias=bias)
+ self.v_proj = nn.Linear(hidden_size, hidden_size, bias=bias)
+ self.q_proj = nn.Linear(hidden_size, hidden_size, bias=bias)
+ self.o_proj = nn.Linear(hidden_size, hidden_size, bias=bias)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ key_value_states: torch.Tensor,
+ attention_mask: torch.Tensor | None = None,
+ position_embeddings: torch.Tensor | None = None,
+ encoder_position_embeddings: torch.Tensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple[torch.Tensor, torch.Tensor]:
+ """
+ Position embeddings logic:
+ - Queries get position_embeddings
+ - Keys get encoder_position_embeddings
+ - Values don't get any position embeddings
+ """
+ query_input_shape = hidden_states.shape[:-1]
+ query_hidden_shape = (*query_input_shape, -1, self.head_dim)
+
+ kv_input_shape = key_value_states.shape[:-1]
+ kv_hidden_shape = (*kv_input_shape, -1, self.head_dim)
+
+ query_input = hidden_states + position_embeddings if position_embeddings is not None else hidden_states
+ key_input = (
+ key_value_states + encoder_position_embeddings
+ if encoder_position_embeddings is not None
+ else key_value_states
+ )
+
+ query_states = self.q_proj(query_input).view(query_hidden_shape).transpose(1, 2)
+ key_states = self.k_proj(key_input).view(kv_hidden_shape).transpose(1, 2)
+ value_states = self.v_proj(key_value_states).view(kv_hidden_shape).transpose(1, 2)
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ query_states,
+ key_states,
+ value_states,
+ attention_mask,
+ dropout=0.0 if not self.training else self.attention_dropout,
+ scaling=self.scaling,
+ **kwargs,
+ )
+
+ attn_output = attn_output.reshape(*query_input_shape, -1).contiguous()
+ attn_output = self.o_proj(attn_output)
+ return attn_output, attn_weights
+
+
+class DetrMLP(nn.Module):
+ def __init__(self, config: DetrConfig, hidden_size: int, intermediate_size: int):
+ super().__init__()
+ self.fc1 = nn.Linear(hidden_size, intermediate_size)
+ self.fc2 = nn.Linear(intermediate_size, hidden_size)
+ self.activation_fn = ACT2FN[config.activation_function]
+ self.activation_dropout = config.activation_dropout
+ self.dropout = config.dropout
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ hidden_states = self.activation_fn(self.fc1(hidden_states))
+ hidden_states = nn.functional.dropout(hidden_states, p=self.activation_dropout, training=self.training)
+ hidden_states = self.fc2(hidden_states)
+ hidden_states = nn.functional.dropout(hidden_states, p=self.dropout, training=self.training)
+ return hidden_states
+
+
+class DetrEncoderLayer(GradientCheckpointingLayer):
+ def __init__(self, config: DetrConfig):
+ super().__init__()
+ self.hidden_size = config.d_model
+ self.self_attn = DetrSelfAttention(
+ config=config,
+ hidden_size=self.hidden_size,
+ num_attention_heads=config.encoder_attention_heads,
+ dropout=config.attention_dropout,
+ )
+ self.self_attn_layer_norm = nn.LayerNorm(self.hidden_size)
+ self.dropout = config.dropout
+ self.mlp = DetrMLP(config, self.hidden_size, config.encoder_ffn_dim)
+ self.final_layer_norm = nn.LayerNorm(self.hidden_size)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ attention_mask: torch.Tensor,
+ spatial_position_embeddings: torch.Tensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> torch.Tensor:
+ """
+ Args:
+ hidden_states (`torch.FloatTensor`): input to the layer of shape `(batch, seq_len, hidden_size)`
+ attention_mask (`torch.FloatTensor`): attention mask of size
+ `(batch, 1, target_len, source_len)` where padding elements are indicated by very large negative
+ values.
+ spatial_position_embeddings (`torch.FloatTensor`, *optional*):
+ Spatial position embeddings (2D positional encodings of image locations), to be added to both
+ the queries and keys in self-attention (but not to values).
+ """
+ residual = hidden_states
+ hidden_states, _ = self.self_attn(
+ hidden_states=hidden_states,
+ attention_mask=attention_mask,
+ position_embeddings=spatial_position_embeddings,
+ **kwargs,
+ )
+
+ hidden_states = nn.functional.dropout(hidden_states, p=self.dropout, training=self.training)
+ hidden_states = residual + hidden_states
+ hidden_states = self.self_attn_layer_norm(hidden_states)
+
+ residual = hidden_states
+ hidden_states = self.mlp(hidden_states)
+ hidden_states = residual + hidden_states
+ hidden_states = self.final_layer_norm(hidden_states)
+
+ if self.training:
+ if not torch.isfinite(hidden_states).all():
+ clamp_value = torch.finfo(hidden_states.dtype).max - 1000
+ hidden_states = torch.clamp(hidden_states, min=-clamp_value, max=clamp_value)
+
+ return hidden_states
+
+
+class DetrDecoderLayer(GradientCheckpointingLayer):
+ def __init__(self, config: DetrConfig):
+ super().__init__()
+ self.hidden_size = config.d_model
+
+ self.self_attn = DetrSelfAttention(
+ config=config,
+ hidden_size=self.hidden_size,
+ num_attention_heads=config.decoder_attention_heads,
+ dropout=config.attention_dropout,
+ )
+ self.dropout = config.dropout
+
+ self.self_attn_layer_norm = nn.LayerNorm(self.hidden_size)
+ self.encoder_attn = DetrCrossAttention(
+ config=config,
+ hidden_size=self.hidden_size,
+ num_attention_heads=config.decoder_attention_heads,
+ dropout=config.attention_dropout,
+ )
+ self.encoder_attn_layer_norm = nn.LayerNorm(self.hidden_size)
+ self.mlp = DetrMLP(config, self.hidden_size, config.decoder_ffn_dim)
+ self.final_layer_norm = nn.LayerNorm(self.hidden_size)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ attention_mask: torch.Tensor | None = None,
+ spatial_position_embeddings: torch.Tensor | None = None,
+ object_queries_position_embeddings: torch.Tensor | None = None,
+ encoder_hidden_states: torch.Tensor | None = None,
+ encoder_attention_mask: torch.Tensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> torch.Tensor:
+ """
+ Args:
+ hidden_states (`torch.FloatTensor`): input to the layer of shape `(batch, seq_len, hidden_size)`
+ attention_mask (`torch.FloatTensor`): attention mask of size
+ `(batch, 1, target_len, source_len)` where padding elements are indicated by very large negative
+ values.
+ spatial_position_embeddings (`torch.FloatTensor`, *optional*):
+ Spatial position embeddings (2D positional encodings from encoder) that are added to the keys only
+ in the cross-attention layer (not to values).
+ object_queries_position_embeddings (`torch.FloatTensor`, *optional*):
+ Position embeddings for the object query slots. In self-attention, these are added to both queries
+ and keys (not values). In cross-attention, these are added to queries only (not to keys or values).
+ encoder_hidden_states (`torch.FloatTensor`):
+ cross attention input to the layer of shape `(batch, seq_len, hidden_size)`
+ encoder_attention_mask (`torch.FloatTensor`): encoder attention mask of size
+ `(batch, 1, target_len, source_len)` where padding elements are indicated by very large negative
+ values.
+ """
+ residual = hidden_states
+
+ # Self Attention
+ hidden_states, _ = self.self_attn(
+ hidden_states=hidden_states,
+ position_embeddings=object_queries_position_embeddings,
+ attention_mask=attention_mask,
+ **kwargs,
+ )
+
+ hidden_states = nn.functional.dropout(hidden_states, p=self.dropout, training=self.training)
+ hidden_states = residual + hidden_states
+ hidden_states = self.self_attn_layer_norm(hidden_states)
+
+ # Cross-Attention Block
+ if encoder_hidden_states is not None:
+ residual = hidden_states
+
+ hidden_states, _ = self.encoder_attn(
+ hidden_states=hidden_states,
+ key_value_states=encoder_hidden_states,
+ attention_mask=encoder_attention_mask,
+ position_embeddings=object_queries_position_embeddings,
+ encoder_position_embeddings=spatial_position_embeddings,
+ **kwargs,
+ )
+
+ hidden_states = nn.functional.dropout(hidden_states, p=self.dropout, training=self.training)
+ hidden_states = residual + hidden_states
+ hidden_states = self.encoder_attn_layer_norm(hidden_states)
+
+ # Fully Connected
+ residual = hidden_states
+ hidden_states = self.mlp(hidden_states)
+ hidden_states = residual + hidden_states
+ hidden_states = self.final_layer_norm(hidden_states)
+
+ return hidden_states
+
+
+class DetrConvBlock(nn.Module):
+ """Basic conv block: Conv3x3 -> GroupNorm -> Activation."""
+
+ def __init__(self, in_channels: int, out_channels: int, activation: str = "relu"):
+ super().__init__()
+ self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1)
+ self.norm = nn.GroupNorm(min(8, out_channels), out_channels)
+ self.activation = ACT2FN[activation]
+
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
+ return self.activation(self.norm(self.conv(x)))
+
+
+class DetrFPNFusionStage(nn.Module):
+ """Single FPN fusion stage combining low-resolution features with high-resolution FPN features."""
+
+ def __init__(self, fpn_channels: int, current_channels: int, output_channels: int, activation: str = "relu"):
+ super().__init__()
+ self.fpn_adapter = nn.Conv2d(fpn_channels, current_channels, kernel_size=1)
+ self.refine = DetrConvBlock(current_channels, output_channels, activation)
+
+ def forward(self, features: torch.Tensor, fpn_features: torch.Tensor) -> torch.Tensor:
+ """
+ Args:
+ features: Current features to upsample, shape (B*Q, current_channels, H_in, W_in)
+ fpn_features: FPN features at target resolution, shape (B*Q, fpn_channels, H_out, W_out)
+
+ Returns:
+ Fused and refined features, shape (B*Q, output_channels, H_out, W_out)
+ """
+ fpn_features = self.fpn_adapter(fpn_features)
+ features = nn.functional.interpolate(features, size=fpn_features.shape[-2:], mode="nearest")
+ return self.refine(fpn_features + features)
+
+
+class DetrMaskHeadSmallConv(nn.Module):
+ """
+ Segmentation mask head that generates per-query masks using FPN-based progressive upsampling.
+
+ Combines attention maps (spatial localization) with encoder features (semantics) and progressively
+ upsamples through multiple scales, fusing with FPN features for high-resolution detail.
+ """
+
+ def __init__(
+ self,
+ input_channels: int,
+ fpn_channels: list[int],
+ hidden_size: int,
+ activation_function: str = "relu",
+ ):
+ super().__init__()
+ if input_channels % 8 != 0:
+ raise ValueError(f"input_channels must be divisible by 8, got {input_channels}")
+
+ self.conv1 = DetrConvBlock(input_channels, input_channels, activation_function)
+ self.conv2 = DetrConvBlock(input_channels, hidden_size // 2, activation_function)
+
+ # Progressive channel reduction: /2 -> /4 -> /8 -> /16
+ self.fpn_stages = nn.ModuleList(
+ [
+ DetrFPNFusionStage(fpn_channels[0], hidden_size // 2, hidden_size // 4, activation_function),
+ DetrFPNFusionStage(fpn_channels[1], hidden_size // 4, hidden_size // 8, activation_function),
+ DetrFPNFusionStage(fpn_channels[2], hidden_size // 8, hidden_size // 16, activation_function),
+ ]
+ )
+
+ self.output_conv = nn.Conv2d(hidden_size // 16, 1, kernel_size=3, padding=1)
+
+ def forward(
+ self,
+ features: torch.Tensor,
+ attention_masks: torch.Tensor,
+ fpn_features: list[torch.Tensor],
+ ) -> torch.Tensor:
+ """
+ Args:
+ features: Encoder output features, shape (batch_size, hidden_size, H, W)
+ attention_masks: Cross-attention maps from decoder, shape (batch_size, num_queries, num_heads, H, W)
+ fpn_features: List of 3 FPN features from low to high resolution, each (batch_size, C, H, W)
+
+ Returns:
+ Predicted masks, shape (batch_size * num_queries, 1, output_H, output_W)
+ """
+ num_queries = attention_masks.shape[1]
+
+ # Expand to (batch_size * num_queries) dimension
+ features = features.unsqueeze(1).expand(-1, num_queries, -1, -1, -1).flatten(0, 1)
+ attention_masks = attention_masks.flatten(0, 1)
+ fpn_features = [
+ fpn_feat.unsqueeze(1).expand(-1, num_queries, -1, -1, -1).flatten(0, 1) for fpn_feat in fpn_features
+ ]
+
+ hidden_states = torch.cat([features, attention_masks], dim=1)
+ hidden_states = self.conv1(hidden_states)
+ hidden_states = self.conv2(hidden_states)
+
+ for fpn_stage, fpn_feat in zip(self.fpn_stages, fpn_features):
+ hidden_states = fpn_stage(hidden_states, fpn_feat)
+
+ return self.output_conv(hidden_states)
+
+
+class DetrMHAttentionMap(nn.Module):
+ """This is a 2D attention module, which only returns the attention softmax (no multiplication by value)"""
+
+ def __init__(
+ self,
+ hidden_size: int,
+ num_attention_heads: int,
+ dropout: float = 0.0,
+ bias: bool = True,
+ ):
+ super().__init__()
+ self.head_dim = hidden_size // num_attention_heads
+ self.scaling = self.head_dim**-0.5
+ self.attention_dropout = dropout
+
+ self.q_proj = nn.Linear(hidden_size, hidden_size, bias=bias)
+ self.k_proj = nn.Linear(hidden_size, hidden_size, bias=bias)
+
+ def forward(
+ self, query_states: torch.Tensor, key_states: torch.Tensor, attention_mask: torch.Tensor | None = None
+ ):
+ query_hidden_shape = (*query_states.shape[:-1], -1, self.head_dim)
+ key_hidden_shape = (key_states.shape[0], -1, self.head_dim, *key_states.shape[-2:])
+
+ query_states = self.q_proj(query_states).view(query_hidden_shape)
+ key_states = nn.functional.conv2d(
+ key_states, self.k_proj.weight.unsqueeze(-1).unsqueeze(-1), self.k_proj.bias
+ ).view(key_hidden_shape)
+
+ batch_size, num_queries, num_heads, head_dim = query_states.shape
+ _, _, _, height, width = key_states.shape
+ query_shape = (batch_size * num_heads, num_queries, head_dim)
+ key_shape = (batch_size * num_heads, height * width, head_dim)
+ attn_weights_shape = (batch_size, num_heads, num_queries, height, width)
+
+ query = query_states.transpose(1, 2).contiguous().view(query_shape)
+ key = key_states.permute(0, 1, 3, 4, 2).contiguous().view(key_shape)
+
+ attn_weights = (
+ (torch.matmul(query * self.scaling, key.transpose(1, 2))).view(attn_weights_shape).transpose(1, 2)
+ )
+
+ if attention_mask is not None:
+ attn_weights = attn_weights + attention_mask
+
+ attn_weights = nn.functional.softmax(attn_weights.flatten(2), dim=-1).view(attn_weights.size())
+ attn_weights = nn.functional.dropout(attn_weights, p=self.attention_dropout, training=self.training)
+
+ return attn_weights
+
+
+@auto_docstring
+class DetrPreTrainedModel(PreTrainedModel):
+ config: DetrConfig
+ base_model_prefix = "model"
+ main_input_name = "pixel_values"
+ input_modalities = ("image",)
+ _no_split_modules = [r"DetrConvEncoder", r"DetrEncoderLayer", r"DetrDecoderLayer"]
+ supports_gradient_checkpointing = True
+ _supports_sdpa = True
+ _supports_flash_attn = True
+ _supports_attention_backend = True
+ _supports_flex_attn = True # Uses create_bidirectional_masks for attention masking
+ _keys_to_ignore_on_load_unexpected = [
+ r"detr\.model\.backbone\.model\.layer\d+\.0\.downsample\.1\.num_batches_tracked"
+ ]
+
+ @torch.no_grad()
+ def _init_weights(self, module):
+ std = self.config.init_std
+ xavier_std = self.config.init_xavier_std
+
+ if isinstance(module, DetrMaskHeadSmallConv):
+ # DetrMaskHeadSmallConv uses kaiming initialization for all its Conv2d layers
+ for m in module.modules():
+ if isinstance(m, nn.Conv2d):
+ init.kaiming_uniform_(m.weight, a=1)
+ if m.bias is not None:
+ init.constant_(m.bias, 0)
+ elif isinstance(module, DetrMHAttentionMap):
+ init.zeros_(module.k_proj.bias)
+ init.zeros_(module.q_proj.bias)
+ init.xavier_uniform_(module.k_proj.weight, gain=xavier_std)
+ init.xavier_uniform_(module.q_proj.weight, gain=xavier_std)
+ elif isinstance(module, DetrLearnedPositionEmbedding):
+ init.uniform_(module.row_embeddings.weight)
+ init.uniform_(module.column_embeddings.weight)
+ elif isinstance(module, (nn.Linear, nn.Conv2d)):
+ init.normal_(module.weight, mean=0.0, std=std)
+ if module.bias is not None:
+ init.zeros_(module.bias)
+ elif isinstance(module, nn.Embedding):
+ init.normal_(module.weight, mean=0.0, std=std)
+ # Here we need the check explicitly, as we slice the weight in the `zeros_` call, so it looses the flag
+ if module.padding_idx is not None and not getattr(module.weight, "_is_hf_initialized", False):
+ init.zeros_(module.weight[module.padding_idx])
+ elif isinstance(module, (nn.LayerNorm, nn.GroupNorm)):
+ init.ones_(module.weight)
+ init.zeros_(module.bias)
+
+
+class DetrEncoder(DetrPreTrainedModel):
+ """
+ Transformer encoder that processes a flattened feature map from a vision backbone, composed of a stack of
+ [`DetrEncoderLayer`] modules.
+
+ Args:
+ config (`DetrConfig`): Model configuration object.
+ """
+
+ _can_record_outputs = {"hidden_states": DetrEncoderLayer, "attentions": DetrSelfAttention}
+
+ def __init__(self, config: DetrConfig):
+ super().__init__(config)
+
+ self.dropout = config.dropout
+ self.layers = nn.ModuleList([DetrEncoderLayer(config) for _ in range(config.encoder_layers)])
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @merge_with_config_defaults
+ @capture_outputs
+ def forward(
+ self,
+ inputs_embeds=None,
+ attention_mask=None,
+ spatial_position_embeddings=None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> BaseModelOutput:
+ r"""
+ Args:
+ inputs_embeds (`torch.FloatTensor` of shape `(batch_size, sequence_length, hidden_size)`):
+ Flattened feature map (output of the backbone + projection layer) that is passed to the encoder.
+ attention_mask (`torch.Tensor` of shape `(batch_size, sequence_length)`, *optional*):
+ Mask to avoid performing attention on padding pixel features. Mask values selected in `[0, 1]`:
+
+ - 1 for pixel features that are real (i.e. **not masked**),
+ - 0 for pixel features that are padding (i.e. **masked**).
+
+ [What are attention masks?](../glossary#attention-mask)
+ spatial_position_embeddings (`torch.FloatTensor` of shape `(batch_size, sequence_length, hidden_size)`):
+ Spatial position embeddings (2D positional encodings) that are added to the queries and keys in each self-attention layer.
+ """
+ hidden_states = inputs_embeds
+ hidden_states = nn.functional.dropout(hidden_states, p=self.dropout, training=self.training)
+
+ attention_mask = create_bidirectional_mask(
+ config=self.config,
+ inputs_embeds=inputs_embeds,
+ attention_mask=attention_mask,
+ )
+
+ for encoder_layer in self.layers:
+ # we add spatial_position_embeddings as extra input to the encoder_layer
+ hidden_states = encoder_layer(
+ hidden_states, attention_mask, spatial_position_embeddings=spatial_position_embeddings, **kwargs
+ )
+
+ return BaseModelOutput(last_hidden_state=hidden_states)
+
+
+class DetrDecoder(DetrPreTrainedModel):
+ """
+ Transformer decoder that refines a set of object queries. It is composed of a stack of [`DetrDecoderLayer`] modules,
+ which apply self-attention to the queries and cross-attention to the encoder's outputs.
+
+ Args:
+ config (`DetrConfig`): Model configuration object.
+ """
+
+ _can_record_outputs = {
+ "hidden_states": DetrDecoderLayer,
+ "attentions": DetrSelfAttention,
+ "cross_attentions": DetrCrossAttention,
+ }
+
+ def __init__(self, config: DetrConfig):
+ super().__init__(config)
+ self.dropout = config.dropout
+
+ self.layers = nn.ModuleList([DetrDecoderLayer(config) for _ in range(config.decoder_layers)])
+ # in DETR, the decoder uses layernorm after the last decoder layer output
+ self.layernorm = nn.LayerNorm(config.d_model)
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @merge_with_config_defaults
+ @capture_outputs
+ def forward(
+ self,
+ inputs_embeds=None,
+ attention_mask=None,
+ encoder_hidden_states=None,
+ encoder_attention_mask=None,
+ spatial_position_embeddings=None,
+ object_queries_position_embeddings=None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> DetrDecoderOutput:
+ r"""
+ Args:
+ inputs_embeds (`torch.FloatTensor` of shape `(batch_size, sequence_length, hidden_size)`):
+ The query embeddings that are passed into the decoder.
+
+ attention_mask (`torch.Tensor` of shape `(batch_size, sequence_length)`, *optional*):
+ Mask to avoid performing attention on certain queries. Mask values selected in `[0, 1]`:
+
+ - 1 for queries that are **not masked**,
+ - 0 for queries that are **masked**.
+
+ [What are attention masks?](../glossary#attention-mask)
+ encoder_hidden_states (`torch.FloatTensor` of shape `(batch_size, encoder_sequence_length, hidden_size)`, *optional*):
+ Sequence of hidden-states at the output of the last layer of the encoder. Used in the cross-attention
+ of the decoder.
+ encoder_attention_mask (`torch.LongTensor` of shape `(batch_size, encoder_sequence_length)`, *optional*):
+ Mask to avoid performing cross-attention on padding pixel_values of the encoder. Mask values selected
+ in `[0, 1]`:
+
+ - 1 for pixels that are real (i.e. **not masked**),
+ - 0 for pixels that are padding (i.e. **masked**).
+
+ spatial_position_embeddings (`torch.FloatTensor` of shape `(batch_size, sequence_length, hidden_size)`, *optional*):
+ Spatial position embeddings (2D positional encodings from encoder) that are added to the keys in each cross-attention layer.
+ object_queries_position_embeddings (`torch.FloatTensor` of shape `(batch_size, num_queries, hidden_size)`, *optional*):
+ Position embeddings for the object query slots that are added to the queries and keys in each self-attention layer.
+ """
+
+ if inputs_embeds is not None:
+ hidden_states = inputs_embeds
+
+ if attention_mask is not None:
+ attention_mask = create_bidirectional_mask(
+ config=self.config,
+ inputs_embeds=hidden_states,
+ attention_mask=attention_mask,
+ )
+
+ # expand encoder attention mask (for cross-attention on encoder outputs)
+ if encoder_hidden_states is not None and encoder_attention_mask is not None:
+ encoder_attention_mask = create_bidirectional_mask(
+ config=self.config,
+ inputs_embeds=hidden_states,
+ attention_mask=encoder_attention_mask,
+ encoder_hidden_states=encoder_hidden_states,
+ )
+
+ # optional intermediate hidden states
+ intermediate = () if self.config.auxiliary_loss else None
+
+ # decoder layers
+
+ for idx, decoder_layer in enumerate(self.layers):
+ hidden_states = decoder_layer(
+ hidden_states,
+ attention_mask,
+ spatial_position_embeddings,
+ object_queries_position_embeddings,
+ encoder_hidden_states, # as a positional argument for gradient checkpointing
+ encoder_attention_mask=encoder_attention_mask,
+ **kwargs,
+ )
+
+ if self.config.auxiliary_loss:
+ hidden_states = self.layernorm(hidden_states)
+ intermediate += (hidden_states,)
+
+ # finally, apply layernorm
+ hidden_states = self.layernorm(hidden_states)
+
+ # stack intermediate decoder activations
+ if self.config.auxiliary_loss:
+ intermediate = torch.stack(intermediate)
+
+ return DetrDecoderOutput(last_hidden_state=hidden_states, intermediate_hidden_states=intermediate)
+
+
+@auto_docstring(
+ custom_intro="""
+ The bare DETR Model (consisting of a backbone and encoder-decoder Transformer) outputting raw hidden-states without
+ any specific head on top.
+ """
+)
+class DetrModel(DetrPreTrainedModel):
+ def __init__(self, config: DetrConfig):
+ super().__init__(config)
+
+ self.backbone = DetrConvEncoder(config)
+
+ if config.position_embedding_type == "sine":
+ self.position_embedding = DetrSinePositionEmbedding(config.d_model // 2, normalize=True)
+ elif config.position_embedding_type == "learned":
+ self.position_embedding = DetrLearnedPositionEmbedding(config.d_model // 2)
+ else:
+ raise ValueError(f"Not supported {config.position_embedding_type}")
+ self.query_position_embeddings = nn.Embedding(config.num_queries, config.d_model)
+ self.input_projection = nn.Conv2d(self.backbone.intermediate_channel_sizes[-1], config.d_model, kernel_size=1)
+
+ self.encoder = DetrEncoder(config)
+ self.decoder = DetrDecoder(config)
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ def freeze_backbone(self):
+ for _, param in self.backbone.model.named_parameters():
+ param.requires_grad_(False)
+
+ def unfreeze_backbone(self):
+ for _, param in self.backbone.model.named_parameters():
+ param.requires_grad_(True)
+
+ @auto_docstring
+ @can_return_tuple
+ def forward(
+ self,
+ pixel_values: torch.FloatTensor | None = None,
+ pixel_mask: torch.LongTensor | None = None,
+ decoder_attention_mask: torch.FloatTensor | None = None,
+ encoder_outputs: torch.FloatTensor | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ decoder_inputs_embeds: torch.FloatTensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple[torch.FloatTensor] | DetrModelOutput:
+ r"""
+ decoder_attention_mask (`torch.FloatTensor` of shape `(batch_size, num_queries)`, *optional*):
+ Mask to avoid performing attention on certain object queries in the decoder. Mask values selected in `[0, 1]`:
+
+ - 1 for queries that are **not masked**,
+ - 0 for queries that are **masked**.
+ inputs_embeds (`torch.FloatTensor` of shape `(batch_size, sequence_length, hidden_size)`, *optional*):
+ Optionally, instead of passing the flattened feature map (output of the backbone + projection layer), you
+ can choose to directly pass a flattened representation of an image. Useful for bypassing the vision backbone.
+ decoder_inputs_embeds (`torch.FloatTensor` of shape `(batch_size, num_queries, hidden_size)`, *optional*):
+ Optionally, instead of initializing the queries with a tensor of zeros, you can choose to directly pass an
+ embedded representation. Useful for tasks that require custom query initialization.
+
+ Examples:
+
+ ```python
+ >>> from transformers import AutoImageProcessor, DetrModel
+ >>> from PIL import Image
+ >>> import httpx
+ >>> from io import BytesIO
+
+ >>> url = "http://images.cocodataset.org/val2017/000000039769.jpg"
+ >>> with httpx.stream("GET", url) as response:
+ ... image = Image.open(BytesIO(response.read()))
+
+ >>> image_processor = AutoImageProcessor.from_pretrained("facebook/detr-resnet-50")
+ >>> model = DetrModel.from_pretrained("facebook/detr-resnet-50")
+
+ >>> # prepare image for the model
+ >>> inputs = image_processor(images=image, return_tensors="pt")
+
+ >>> # forward pass
+ >>> outputs = model(**inputs)
+
+ >>> # the last hidden states are the final query embeddings of the Transformer decoder
+ >>> # these are of shape (batch_size, num_queries, hidden_size)
+ >>> last_hidden_states = outputs.last_hidden_state
+ >>> list(last_hidden_states.shape)
+ [1, 100, 256]
+ ```"""
+ if pixel_values is None and inputs_embeds is None:
+ raise ValueError("You have to specify either pixel_values or inputs_embeds")
+
+ if inputs_embeds is None:
+ batch_size, num_channels, height, width = pixel_values.shape
+ device = pixel_values.device
+
+ if pixel_mask is None:
+ pixel_mask = torch.ones(((batch_size, height, width)), device=device)
+ vision_features = self.backbone(pixel_values, pixel_mask)
+ feature_map, mask = vision_features[-1]
+
+ # Apply 1x1 conv to map (batch_size, C, H, W) -> (batch_size, hidden_size, H, W), then flatten to (batch_size, HW, hidden_size)
+ # Position embeddings are already flattened to (batch_size, sequence_length, hidden_size) format
+ projected_feature_map = self.input_projection(feature_map)
+ flattened_features = projected_feature_map.flatten(2).permute(0, 2, 1)
+ spatial_position_embeddings = self.position_embedding(
+ shape=feature_map.shape, device=device, dtype=pixel_values.dtype, mask=mask
+ )
+ flattened_mask = mask.flatten(1)
+ else:
+ batch_size = inputs_embeds.shape[0]
+ device = inputs_embeds.device
+ flattened_features = inputs_embeds
+ # When using inputs_embeds, we need to infer spatial dimensions for position embeddings
+ # Assume square feature map
+ seq_len = inputs_embeds.shape[1]
+ feat_dim = int(seq_len**0.5)
+ # Create position embeddings for the inferred spatial size
+ spatial_position_embeddings = self.position_embedding(
+ shape=torch.Size([batch_size, self.config.d_model, feat_dim, feat_dim]),
+ device=device,
+ dtype=inputs_embeds.dtype,
+ )
+ # If a pixel_mask is provided with inputs_embeds, interpolate it to feat_dim, then flatten.
+ if pixel_mask is not None:
+ mask = nn.functional.interpolate(pixel_mask[None].float(), size=(feat_dim, feat_dim)).to(torch.bool)[0]
+ flattened_mask = mask.flatten(1)
+ else:
+ # If no mask provided, assume all positions are valid
+ flattened_mask = torch.ones((batch_size, seq_len), device=device, dtype=torch.long)
+
+ if encoder_outputs is None:
+ encoder_outputs = self.encoder(
+ inputs_embeds=flattened_features,
+ attention_mask=flattened_mask,
+ spatial_position_embeddings=spatial_position_embeddings,
+ **kwargs,
+ )
+
+ object_queries_position_embeddings = self.query_position_embeddings.weight.unsqueeze(0).repeat(
+ batch_size, 1, 1
+ )
+
+ # Use decoder_inputs_embeds as queries if provided, otherwise initialize with zeros
+ if decoder_inputs_embeds is not None:
+ queries = decoder_inputs_embeds
+ else:
+ queries = torch.zeros_like(object_queries_position_embeddings)
+
+ # decoder outputs consists of (dec_features, dec_hidden, dec_attn)
+ decoder_outputs = self.decoder(
+ inputs_embeds=queries,
+ attention_mask=decoder_attention_mask,
+ spatial_position_embeddings=spatial_position_embeddings,
+ object_queries_position_embeddings=object_queries_position_embeddings,
+ encoder_hidden_states=encoder_outputs.last_hidden_state,
+ encoder_attention_mask=flattened_mask,
+ **kwargs,
+ )
+
+ return DetrModelOutput(
+ last_hidden_state=decoder_outputs.last_hidden_state,
+ decoder_hidden_states=decoder_outputs.hidden_states,
+ decoder_attentions=decoder_outputs.attentions,
+ cross_attentions=decoder_outputs.cross_attentions,
+ encoder_last_hidden_state=encoder_outputs.last_hidden_state,
+ encoder_hidden_states=encoder_outputs.hidden_states,
+ encoder_attentions=encoder_outputs.attentions,
+ intermediate_hidden_states=decoder_outputs.intermediate_hidden_states,
+ )
+
+
+class DetrMLPPredictionHead(nn.Module):
+ """
+ Very simple multi-layer perceptron (MLP, also called FFN), used to predict the normalized center coordinates,
+ height and width of a bounding box w.r.t. an image.
+
+ """
+
+ def __init__(self, input_dim, hidden_dim, output_dim, num_layers):
+ super().__init__()
+ self.num_layers = num_layers
+ h = [hidden_dim] * (num_layers - 1)
+ self.layers = nn.ModuleList(nn.Linear(n, k) for n, k in zip([input_dim] + h, h + [output_dim]))
+
+ def forward(self, x):
+ for i, layer in enumerate(self.layers):
+ x = nn.functional.relu(layer(x)) if i < self.num_layers - 1 else layer(x)
+ return x
+
+
+@auto_docstring(
+ custom_intro="""
+ DETR Model (consisting of a backbone and encoder-decoder Transformer) with object detection heads on top, for tasks
+ such as COCO detection.
+ """
+)
+class DetrForObjectDetection(DetrPreTrainedModel):
+ def __init__(self, config: DetrConfig):
+ super().__init__(config)
+
+ # DETR encoder-decoder model
+ self.model = DetrModel(config)
+
+ # Object detection heads
+ self.class_labels_classifier = nn.Linear(
+ config.d_model, config.num_labels + 1
+ ) # We add one for the "no object" class
+ self.bbox_predictor = DetrMLPPredictionHead(
+ input_dim=config.d_model, hidden_dim=config.d_model, output_dim=4, num_layers=3
+ )
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @auto_docstring
+ @can_return_tuple
+ def forward(
+ self,
+ pixel_values: torch.FloatTensor,
+ pixel_mask: torch.LongTensor | None = None,
+ decoder_attention_mask: torch.FloatTensor | None = None,
+ encoder_outputs: torch.FloatTensor | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ decoder_inputs_embeds: torch.FloatTensor | None = None,
+ labels: list[dict] | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple[torch.FloatTensor] | DetrObjectDetectionOutput:
+ r"""
+ decoder_attention_mask (`torch.FloatTensor` of shape `(batch_size, num_queries)`, *optional*):
+ Mask to avoid performing attention on certain object queries in the decoder. Mask values selected in `[0, 1]`:
+
+ - 1 for queries that are **not masked**,
+ - 0 for queries that are **masked**.
+ inputs_embeds (`torch.FloatTensor` of shape `(batch_size, sequence_length, hidden_size)`, *optional*):
+ Optionally, instead of passing the flattened feature map (output of the backbone + projection layer), you
+ can choose to directly pass a flattened representation of an image. Useful for bypassing the vision backbone.
+ decoder_inputs_embeds (`torch.FloatTensor` of shape `(batch_size, num_queries, hidden_size)`, *optional*):
+ Optionally, instead of initializing the queries with a tensor of zeros, you can choose to directly pass an
+ embedded representation. Useful for tasks that require custom query initialization.
+ labels (`list[Dict]` of len `(batch_size,)`, *optional*):
+ Labels for computing the bipartite matching loss. List of dicts, each dictionary containing at least the
+ following 2 keys: 'class_labels' and 'boxes' (the class labels and bounding boxes of an image in the batch
+ respectively). The class labels themselves should be a `torch.LongTensor` of len `(number of bounding boxes
+ in the image,)` and the boxes a `torch.FloatTensor` of shape `(number of bounding boxes in the image, 4)`.
+
+ Examples:
+
+ ```python
+ >>> from transformers import AutoImageProcessor, DetrForObjectDetection
+ >>> import torch
+ >>> from PIL import Image
+ >>> import httpx
+ >>> from io import BytesIO
+
+ >>> url = "http://images.cocodataset.org/val2017/000000039769.jpg"
+ >>> with httpx.stream("GET", url) as response:
+ ... image = Image.open(BytesIO(response.read()))
+
+ >>> image_processor = AutoImageProcessor.from_pretrained("facebook/detr-resnet-50")
+ >>> model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50")
+
+ >>> inputs = image_processor(images=image, return_tensors="pt")
+ >>> outputs = model(**inputs)
+
+ >>> # convert outputs (bounding boxes and class logits) to Pascal VOC format (xmin, ymin, xmax, ymax)
+ >>> target_sizes = torch.tensor([image.size[::-1]])
+ >>> results = image_processor.post_process_object_detection(outputs, threshold=0.9, target_sizes=target_sizes)[
+ ... 0
+ ... ]
+
+ >>> for score, label, box in zip(results["scores"], results["labels"], results["boxes"]):
+ ... box = [round(i, 2) for i in box.tolist()]
+ ... print(
+ ... f"Detected {model.config.id2label[label.item()]} with confidence "
+ ... f"{round(score.item(), 3)} at location {box}"
+ ... )
+ Detected remote with confidence 0.998 at location [40.16, 70.81, 175.55, 117.98]
+ Detected remote with confidence 0.996 at location [333.24, 72.55, 368.33, 187.66]
+ Detected couch with confidence 0.995 at location [-0.02, 1.15, 639.73, 473.76]
+ Detected cat with confidence 0.999 at location [13.24, 52.05, 314.02, 470.93]
+ Detected cat with confidence 0.999 at location [345.4, 23.85, 640.37, 368.72]
+ ```"""
+
+ # First, sent images through DETR base model to obtain encoder + decoder outputs
+ outputs = self.model(
+ pixel_values,
+ pixel_mask=pixel_mask,
+ decoder_attention_mask=decoder_attention_mask,
+ encoder_outputs=encoder_outputs,
+ inputs_embeds=inputs_embeds,
+ decoder_inputs_embeds=decoder_inputs_embeds,
+ **kwargs,
+ )
+
+ sequence_output = outputs[0]
+
+ # class logits + predicted bounding boxes
+ logits = self.class_labels_classifier(sequence_output)
+ pred_boxes = self.bbox_predictor(sequence_output).sigmoid()
+
+ loss, loss_dict, auxiliary_outputs = None, None, None
+ if labels is not None:
+ outputs_class, outputs_coord = None, None
+ if self.config.auxiliary_loss:
+ intermediate = outputs.intermediate_hidden_states
+ outputs_class = self.class_labels_classifier(intermediate)
+ outputs_coord = self.bbox_predictor(intermediate).sigmoid()
+ loss, loss_dict, auxiliary_outputs = self.loss_function(
+ logits, labels, self.device, pred_boxes, self.config, outputs_class, outputs_coord
+ )
+
+ return DetrObjectDetectionOutput(
+ loss=loss,
+ loss_dict=loss_dict,
+ logits=logits,
+ pred_boxes=pred_boxes,
+ auxiliary_outputs=auxiliary_outputs,
+ last_hidden_state=outputs.last_hidden_state,
+ decoder_hidden_states=outputs.decoder_hidden_states,
+ decoder_attentions=outputs.decoder_attentions,
+ cross_attentions=outputs.cross_attentions,
+ encoder_last_hidden_state=outputs.encoder_last_hidden_state,
+ encoder_hidden_states=outputs.encoder_hidden_states,
+ encoder_attentions=outputs.encoder_attentions,
+ )
+
+
+@auto_docstring(
+ custom_intro="""
+ DETR Model (consisting of a backbone and encoder-decoder Transformer) with a segmentation head on top, for tasks
+ such as COCO panoptic.
+ """
+)
+class DetrForSegmentation(DetrPreTrainedModel):
+ def __init__(self, config: DetrConfig):
+ super().__init__(config)
+
+ # object detection model
+ self.detr = DetrForObjectDetection(config)
+
+ # segmentation head
+ hidden_size, number_of_heads = config.d_model, config.encoder_attention_heads
+ intermediate_channel_sizes = self.detr.model.backbone.intermediate_channel_sizes
+
+ self.mask_head = DetrMaskHeadSmallConv(
+ input_channels=hidden_size + number_of_heads,
+ fpn_channels=intermediate_channel_sizes[::-1][-3:],
+ hidden_size=hidden_size,
+ activation_function=config.activation_function,
+ )
+
+ self.bbox_attention = DetrMHAttentionMap(hidden_size, number_of_heads, dropout=0.0)
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @auto_docstring
+ @can_return_tuple
+ def forward(
+ self,
+ pixel_values: torch.FloatTensor,
+ pixel_mask: torch.LongTensor | None = None,
+ decoder_attention_mask: torch.FloatTensor | None = None,
+ encoder_outputs: torch.FloatTensor | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ decoder_inputs_embeds: torch.FloatTensor | None = None,
+ labels: list[dict] | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple[torch.FloatTensor] | DetrSegmentationOutput:
+ r"""
+ decoder_attention_mask (`torch.FloatTensor` of shape `(batch_size, num_queries)`, *optional*):
+ Mask to avoid performing attention on certain object queries in the decoder. Mask values selected in `[0, 1]`:
+
+ - 1 for queries that are **not masked**,
+ - 0 for queries that are **masked**.
+ inputs_embeds (`torch.FloatTensor` of shape `(batch_size, sequence_length, hidden_size)`, *optional*):
+ Kept for backward compatibility, but cannot be used for segmentation, as segmentation requires
+ multi-scale features from the backbone that are not available when bypassing it with inputs_embeds.
+ decoder_inputs_embeds (`torch.FloatTensor` of shape `(batch_size, num_queries, hidden_size)`, *optional*):
+ Optionally, instead of initializing the queries with a tensor of zeros, you can choose to directly pass an
+ embedded representation. Useful for tasks that require custom query initialization.
+ labels (`list[Dict]` of len `(batch_size,)`, *optional*):
+ Labels for computing the bipartite matching loss, DICE/F-1 loss and Focal loss. List of dicts, each
+ dictionary containing at least the following 3 keys: 'class_labels', 'boxes' and 'masks' (the class labels,
+ bounding boxes and segmentation masks of an image in the batch respectively). The class labels themselves
+ should be a `torch.LongTensor` of len `(number of bounding boxes in the image,)`, the boxes a
+ `torch.FloatTensor` of shape `(number of bounding boxes in the image, 4)` and the masks a
+ `torch.FloatTensor` of shape `(number of bounding boxes in the image, height, width)`.
+
+ Examples:
+
+ ```python
+ >>> import io
+ >>> import httpx
+ >>> from io import BytesIO
+ >>> from PIL import Image
+ >>> import torch
+ >>> import numpy
+
+ >>> from transformers import AutoImageProcessor, DetrForSegmentation
+ >>> from transformers.image_transforms import rgb_to_id
+
+ >>> url = "http://images.cocodataset.org/val2017/000000039769.jpg"
+ >>> with httpx.stream("GET", url) as response:
+ ... image = Image.open(BytesIO(response.read()))
+
+ >>> image_processor = AutoImageProcessor.from_pretrained("facebook/detr-resnet-50-panoptic")
+ >>> model = DetrForSegmentation.from_pretrained("facebook/detr-resnet-50-panoptic")
+
+ >>> # prepare image for the model
+ >>> inputs = image_processor(images=image, return_tensors="pt")
+
+ >>> # forward pass
+ >>> outputs = model(**inputs)
+
+ >>> # Use the `post_process_panoptic_segmentation` method of the `image_processor` to retrieve post-processed panoptic segmentation maps
+ >>> # Segmentation results are returned as a list of dictionaries
+ >>> result = image_processor.post_process_panoptic_segmentation(outputs, target_sizes=[(300, 500)])
+
+ >>> # A tensor of shape (height, width) where each value denotes a segment id, filled with -1 if no segment is found
+ >>> panoptic_seg = result[0]["segmentation"]
+ >>> panoptic_seg.shape
+ torch.Size([300, 500])
+ >>> # Get prediction score and segment_id to class_id mapping of each segment
+ >>> panoptic_segments_info = result[0]["segments_info"]
+ >>> len(panoptic_segments_info)
+ 5
+ ```"""
+
+ batch_size, num_channels, height, width = pixel_values.shape
+ device = pixel_values.device
+
+ if pixel_mask is None:
+ pixel_mask = torch.ones((batch_size, height, width), device=device)
+
+ vision_features = self.detr.model.backbone(pixel_values, pixel_mask)
+ feature_map, mask = vision_features[-1]
+
+ # Apply 1x1 conv to map (batch_size, C, H, W) -> (batch_size, hidden_size, H, W), then flatten to (batch_size, HW, hidden_size)
+ projected_feature_map = self.detr.model.input_projection(feature_map)
+ flattened_features = projected_feature_map.flatten(2).permute(0, 2, 1)
+ spatial_position_embeddings = self.detr.model.position_embedding(
+ shape=feature_map.shape, device=device, dtype=pixel_values.dtype, mask=mask
+ )
+ flattened_mask = mask.flatten(1)
+
+ if encoder_outputs is None:
+ encoder_outputs = self.detr.model.encoder(
+ inputs_embeds=flattened_features,
+ attention_mask=flattened_mask,
+ spatial_position_embeddings=spatial_position_embeddings,
+ **kwargs,
+ )
+
+ object_queries_position_embeddings = self.detr.model.query_position_embeddings.weight.unsqueeze(0).repeat(
+ batch_size, 1, 1
+ )
+
+ # Use decoder_inputs_embeds as queries if provided, otherwise initialize with zeros
+ if decoder_inputs_embeds is not None:
+ queries = decoder_inputs_embeds
+ else:
+ queries = torch.zeros_like(object_queries_position_embeddings)
+
+ decoder_outputs = self.detr.model.decoder(
+ inputs_embeds=queries,
+ attention_mask=decoder_attention_mask,
+ spatial_position_embeddings=spatial_position_embeddings,
+ object_queries_position_embeddings=object_queries_position_embeddings,
+ encoder_hidden_states=encoder_outputs.last_hidden_state,
+ encoder_attention_mask=flattened_mask,
+ **kwargs,
+ )
+
+ sequence_output = decoder_outputs[0]
+
+ logits = self.detr.class_labels_classifier(sequence_output)
+ pred_boxes = self.detr.bbox_predictor(sequence_output).sigmoid()
+
+ height, width = feature_map.shape[-2:]
+ memory = encoder_outputs.last_hidden_state.permute(0, 2, 1).view(
+ batch_size, self.config.d_model, height, width
+ )
+ attention_mask = flattened_mask.view(batch_size, height, width)
+
+ if attention_mask is not None:
+ min_dtype = torch.finfo(memory.dtype).min
+ attention_mask = torch.where(
+ attention_mask.unsqueeze(1).unsqueeze(1),
+ torch.tensor(0.0, device=memory.device, dtype=memory.dtype),
+ min_dtype,
+ )
+
+ bbox_mask = self.bbox_attention(sequence_output, memory, attention_mask=attention_mask)
+
+ seg_masks = self.mask_head(
+ features=projected_feature_map,
+ attention_masks=bbox_mask,
+ fpn_features=[vision_features[2][0], vision_features[1][0], vision_features[0][0]],
+ )
+
+ pred_masks = seg_masks.view(batch_size, self.detr.config.num_queries, seg_masks.shape[-2], seg_masks.shape[-1])
+
+ loss, loss_dict, auxiliary_outputs = None, None, None
+ if labels is not None:
+ outputs_class, outputs_coord = None, None
+ if self.config.auxiliary_loss:
+ intermediate = decoder_outputs.intermediate_hidden_states
+ outputs_class = self.detr.class_labels_classifier(intermediate)
+ outputs_coord = self.detr.bbox_predictor(intermediate).sigmoid()
+ loss, loss_dict, auxiliary_outputs = self.loss_function(
+ logits, labels, device, pred_boxes, pred_masks, self.config, outputs_class, outputs_coord
+ )
+
+ return DetrSegmentationOutput(
+ loss=loss,
+ loss_dict=loss_dict,
+ logits=logits,
+ pred_boxes=pred_boxes,
+ pred_masks=pred_masks,
+ auxiliary_outputs=auxiliary_outputs,
+ last_hidden_state=decoder_outputs.last_hidden_state,
+ decoder_hidden_states=decoder_outputs.hidden_states,
+ decoder_attentions=decoder_outputs.attentions,
+ cross_attentions=decoder_outputs.cross_attentions,
+ encoder_last_hidden_state=encoder_outputs.last_hidden_state,
+ encoder_hidden_states=encoder_outputs.hidden_states,
+ encoder_attentions=encoder_outputs.attentions,
+ )
+
+
+__all__ = [
+ "DetrForObjectDetection",
+ "DetrForSegmentation",
+ "DetrModel",
+ "DetrPreTrainedModel",
+]
diff --git a/third_party/transformers/src/transformers/models/dia/__init__.py b/third_party/transformers/src/transformers/models/dia/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..d738fbc087888597da19735271366d4e35ab708c
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/dia/__init__.py
@@ -0,0 +1,31 @@
+# Copyright 2025 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_dia import *
+ from .feature_extraction_dia import *
+ from .generation_dia import *
+ from .modeling_dia import *
+ from .processing_dia import *
+ from .tokenization_dia import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/dia/configuration_dia.py b/third_party/transformers/src/transformers/models/dia/configuration_dia.py
new file mode 100644
index 0000000000000000000000000000000000000000..8bf71724b09114fdaf82d667281bee6247e7bb39
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/dia/configuration_dia.py
@@ -0,0 +1,169 @@
+# Copyright 2025 The Nari Labs and HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Dia model configuration"""
+
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...modeling_rope_utils import RopeParameters
+from ...utils import auto_docstring, logging
+
+
+logger = logging.get_logger(__name__)
+
+
+@auto_docstring(checkpoint="nari-labs/Dia-1.6B")
+@strict
+class DiaEncoderConfig(PreTrainedConfig):
+ model_type = "dia_encoder"
+
+ max_position_embeddings: int = 1024
+ num_hidden_layers: int = 12
+ hidden_size: int = 1024
+ num_attention_heads: int = 16
+ num_key_value_heads: int = 16
+ head_dim: int = 128
+ intermediate_size: int = 4096
+ norm_eps: float = 1e-5
+ vocab_size: int = 256
+ hidden_act: str = "silu"
+ rope_parameters: dict | None = None
+ initializer_range: float = 0.02
+
+
+@auto_docstring(checkpoint="nari-labs/Dia-1.6B")
+@strict
+class DiaDecoderConfig(PreTrainedConfig):
+ r"""
+ cross_num_attention_heads (`int`, *optional*, defaults to 16):
+ Number of attention heads for each cross-attention layer in the Transformer decoder.
+ cross_head_dim (`int`, *optional*, defaults to 128):
+ Dimensionality of the cross-attention head.
+ cross_num_key_value_heads (`int`, *optional*, defaults to 16):
+ Number of key and value heads for each cross-attention layer in the Transformer decoder.
+ cross_hidden_size (`int`, *optional*, defaults to 1024):
+ Dimensionality of the cross-attention layers.
+ """
+
+ model_type = "dia_decoder"
+
+ max_position_embeddings: int = 3072
+ num_hidden_layers: int = 18
+ hidden_size: int = 2048
+ intermediate_size: int = 8192
+ num_attention_heads: int = 16
+ num_key_value_heads: int = 4
+ head_dim: int = 128
+ cross_num_attention_heads: int = 16
+ cross_head_dim: int = 128
+ cross_num_key_value_heads: int = 16
+ cross_hidden_size: int = 1024
+ norm_eps: float = 1e-5
+ vocab_size: int = 1028
+ hidden_act: str = "silu"
+ num_channels: int = 9
+ rope_parameters: RopeParameters | dict | None = None
+ initializer_range: float = 0.02
+ use_cache: bool = True
+ is_encoder_decoder: bool = True
+ pad_token_id: int | None = 1025
+ eos_token_id: int | list[int] | None = 1024
+ bos_token_id: int | None = 1026
+
+
+@auto_docstring(checkpoint="nari-labs/Dia-1.6B")
+@strict
+class DiaConfig(PreTrainedConfig):
+ r"""
+ delay_pattern (`list[int]`, *optional*, defaults to `[0, 8, 9, 10, 11, 12, 13, 14, 15]`):
+ The delay pattern for the decoder. The length of this list must match `decoder_config.num_channels`.
+
+ Example:
+
+ ```python
+ >>> from transformers import DiaConfig, DiaModel
+
+ >>> # Initializing a DiaConfig with default values
+ >>> configuration = DiaConfig()
+
+ >>> # Initializing a DiaModel (with random weights) from the configuration
+ >>> model = DiaModel(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```
+ """
+
+ model_type = "dia"
+ keys_to_ignore_at_inference = ["past_key_values"]
+ sub_configs = {"encoder_config": DiaEncoderConfig, "decoder_config": DiaDecoderConfig}
+
+ encoder_config: DiaEncoderConfig | dict | None = None
+ decoder_config: DiaDecoderConfig | dict | None = None
+ norm_eps: float = 1e-5
+ is_encoder_decoder: bool = True
+ pad_token_id: int | None = None
+ eos_token_id: int | list[int] | None = None
+ bos_token_id: int | None = None
+ delay_pattern: list[int] | None = None
+ initializer_range: float = 0.02
+ use_cache: bool = True
+
+ def __post_init__(self, **kwargs):
+ if isinstance(self.encoder_config, dict):
+ self.encoder_config = DiaEncoderConfig(**self.encoder_config)
+ if isinstance(self.decoder_config, dict):
+ self.decoder_config = DiaDecoderConfig(**self.decoder_config)
+
+ self.encoder_config = self.encoder_config if self.encoder_config is not None else DiaEncoderConfig()
+ self.decoder_config = self.decoder_config if self.decoder_config is not None else DiaDecoderConfig()
+ self.delay_pattern = (
+ self.delay_pattern if self.delay_pattern is not None else [0, 8, 9, 10, 11, 12, 13, 14, 15]
+ )
+
+ # TODO: Remove token ID forwarding once the `nari-labs/Dia-1.6B` checkpoint is updated
+ if self.pad_token_id is not None:
+ logger.warning_once(
+ "Passing `pad_token_id` to `DiaConfig` is deprecated. "
+ "Please set it directly on `DiaDecoderConfig` instead."
+ )
+ self.decoder_config.pad_token_id = self.pad_token_id
+
+ if self.eos_token_id is not None:
+ logger.warning_once(
+ "Passing `eos_token_id` to `DiaConfig` is deprecated. "
+ "Please set it directly on `DiaDecoderConfig` instead."
+ )
+ self.decoder_config.eos_token_id = self.eos_token_id
+
+ if self.bos_token_id is not None:
+ logger.warning_once(
+ "Passing `bos_token_id` to `DiaConfig` is deprecated. "
+ "Please set it directly on `DiaDecoderConfig` instead."
+ )
+ self.decoder_config.bos_token_id = self.bos_token_id
+
+ super().__post_init__(**kwargs)
+
+ def validate_architecture(self):
+ """Part of `@strict`-powered validation. Validates the architecture of the config."""
+ if self.decoder_config.num_channels != len(self.delay_pattern):
+ raise ValueError("Number of channels must match delay pattern length.")
+
+ def get_text_config(self, *args, **kwargs):
+ """Defaulting to audio config as it's the decoder in this case which is usually the text backbone"""
+ return self.decoder_config
+
+
+__all__ = ["DiaConfig", "DiaEncoderConfig", "DiaDecoderConfig"]
diff --git a/third_party/transformers/src/transformers/models/dia/convert_dia_to_hf.py b/third_party/transformers/src/transformers/models/dia/convert_dia_to_hf.py
new file mode 100644
index 0000000000000000000000000000000000000000..067f176e1404ddac7d9c93176fa1c314b2489595
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/dia/convert_dia_to_hf.py
@@ -0,0 +1,198 @@
+# Copyright 2025 The Nari Labs and HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Converts a Dia model in Nari Labs format to Hugging Face format."""
+
+import argparse
+import os
+import re
+
+import torch
+from huggingface_hub import snapshot_download
+from safetensors.torch import load_file
+
+from transformers import (
+ DacModel,
+ DiaConfig,
+ DiaFeatureExtractor,
+ DiaForConditionalGeneration,
+ DiaProcessor,
+ DiaTokenizer,
+ GenerationConfig,
+)
+from transformers.utils.import_utils import is_tiktoken_available
+
+
+# Provide just the list of layer keys you want to fix
+shape_mappings = [
+ "encoder.layers.*.mlp.gate_up_proj.weight",
+ "encoder.layers.*.mlp.down_proj.weight",
+ "encoder.layers.*.self_attention.q_proj.weight",
+ "encoder.layers.*.self_attention.k_proj.weight",
+ "encoder.layers.*.self_attention.v_proj.weight",
+ "encoder.layers.*.self_attention.o_proj.weight",
+ "decoder.layers.*.mlp.gate_up_proj.weight",
+ "decoder.layers.*.mlp.down_proj.weight",
+ "decoder.layers.*.self_attention.q_proj.weight",
+ "decoder.layers.*.self_attention.k_proj.weight",
+ "decoder.layers.*.self_attention.v_proj.weight",
+ "decoder.layers.*.self_attention.o_proj.weight",
+ "decoder.layers.*.cross_attention.q_proj.weight",
+ "decoder.layers.*.cross_attention.k_proj.weight",
+ "decoder.layers.*.cross_attention.v_proj.weight",
+ "decoder.layers.*.cross_attention.o_proj.weight",
+ "decoder.logits_dense.weight",
+]
+
+# Provide renamings here
+rename_mapping = {
+ "mlp.wo": "mlp.down_proj",
+ "mlp.wi_fused": "mlp.gate_up_proj",
+}
+
+
+def get_generation_config(config):
+ model_generation_config = GenerationConfig.from_model_config(config)
+ model_generation_config._from_model_config = False
+ model_generation_config.do_sample = True
+ model_generation_config.top_k = 45
+ model_generation_config.top_p = 0.95
+ model_generation_config.temperature = 1.2
+ model_generation_config.guidance_scale = 3.0
+ model_generation_config.max_length = 3072 # Decoder max length
+
+ return model_generation_config
+
+
+def convert_dia_model_to_hf(checkpoint_path, verbose=False):
+ """
+ Converts a Dia model in Nari Labs format to Hugging Face format.
+ Args:
+ checkpoint_path (`str`):
+ Path to the downloaded checkpoints.
+ verbose (`bool`, *optional*)
+ Whether to print information during conversion.
+ """
+ # Download from HF Hub if checkpoint_path is None
+ checkpoint_path = snapshot_download(repo_id=checkpoint_path, allow_patterns=["*.pth", "*.safetensors"])
+ print(f"Downloaded checkpoint from Hugging Face Hub: {checkpoint_path}")
+
+ # Initialize base model with default config == 1.6B model
+ with torch.device("meta"):
+ hf_model = DiaForConditionalGeneration(config=DiaConfig())
+ hf_model_dict = hf_model.state_dict()
+ hf_model_keys = hf_model_dict.keys()
+
+ # Iterate through dir to catch all respective files - prefers safetensors but allows pt
+ files = os.listdir(checkpoint_path)
+ for file in files:
+ if file.endswith(".safetensors"):
+ load_function = load_file
+ elif file.endswith(".pth"):
+ load_function = torch.load
+ checkpoint_path = os.path.join(checkpoint_path, files[0])
+ nari_state_dict = load_function(checkpoint_path, "cpu")
+
+ # Conversion starts here
+ converted_state_dict = {}
+ embeddings = {}
+ for key, tensor in nari_state_dict.items():
+ # add prefix
+ key = "model." + key
+
+ # rename some weights
+ for original, rename in rename_mapping.items():
+ if original in key:
+ key = re.sub(original, rename, key)
+
+ # decoder multi channel
+ if "embeddings" in key:
+ embeddings_key = key.rsplit(".", 2)[0] + ".embed.weight"
+ if embeddings_key in embeddings:
+ embeddings[embeddings_key] += [tensor]
+ else:
+ embeddings[embeddings_key] = [tensor]
+ continue
+ elif re.sub(r"\d+", "*", key).removeprefix("model.") in shape_mappings:
+ # add exception to the head
+ if "logits_dense" in key:
+ key = re.sub("decoder.logits_dense", "logits_dense", key).removeprefix("model.")
+
+ # dense general
+ if key in hf_model_keys:
+ tensor_shape = tensor.shape
+ target_shape = hf_model_dict[key].shape
+ try:
+ tensor = tensor.reshape(target_shape[1], target_shape[0]).T
+ if verbose:
+ print(f"{key}: transpose reshaped from {tensor_shape} to {target_shape}")
+ except Exception as e:
+ print(f"WARNING: Could not reshape {key}: {e}")
+
+ converted_state_dict[key] = tensor
+
+ # Combining the embeddings as last step
+ embeddings = {k: torch.cat(v, dim=0) for k, v in embeddings.items()}
+ converted_state_dict.update(embeddings)
+
+ # Load converted weights into HF model
+ hf_model.load_state_dict(converted_state_dict, assign=True)
+
+ # Overwrite generation config
+ hf_model.generation_config = get_generation_config(DiaConfig())
+
+ return hf_model
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ # # Required parameters
+ parser.add_argument(
+ "--checkpoint_path", type=str, default="nari-labs/Dia-1.6B", help="Path to the downloaded checkpoints"
+ )
+ parser.add_argument(
+ "--pytorch_dump_folder_path", default="AntonV/Dia-1.6B", type=str, help="Path to the output PyTorch model."
+ )
+ parser.add_argument(
+ "--convert_preprocessor",
+ type=bool,
+ default=True,
+ help="Whether or not the preprocessor (tokenizer + feature extractor) should be converted along with the model.",
+ )
+ parser.add_argument(
+ "--verbose",
+ type=bool,
+ default=True,
+ help="Whether or not to log information during conversion.",
+ )
+ args = parser.parse_args()
+
+ model = convert_dia_model_to_hf(args.checkpoint_path, args.verbose)
+ if args.convert_preprocessor:
+ try:
+ if not is_tiktoken_available(with_blobfile=False):
+ raise ModuleNotFoundError(
+ """`tiktoken` is not installed, use `pip install tiktoken` to convert the tokenizer"""
+ )
+ except Exception as e:
+ print(e)
+ else:
+ processor = DiaProcessor(
+ DiaFeatureExtractor(sampling_rate=44100, hop_length=512),
+ DiaTokenizer(),
+ DacModel.from_pretrained("descript/dac_44khz"),
+ )
+ processor.save_pretrained(args.pytorch_dump_folder_path)
+
+ model.save_pretrained(args.pytorch_dump_folder_path)
+ print(f"Saved converted checkpoint to {args.pytorch_dump_folder_path}")
diff --git a/third_party/transformers/src/transformers/models/dia/feature_extraction_dia.py b/third_party/transformers/src/transformers/models/dia/feature_extraction_dia.py
new file mode 100644
index 0000000000000000000000000000000000000000..eda1ead6e0147f350af0f6cb6833bf15ccc71add
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/dia/feature_extraction_dia.py
@@ -0,0 +1,179 @@
+# Copyright 2025 The Nari Labs and HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Feature extractor class for Dia"""
+
+import numpy as np
+
+from ...feature_extraction_sequence_utils import SequenceFeatureExtractor
+from ...feature_extraction_utils import BatchFeature
+from ...utils import PaddingStrategy, TensorType, logging
+
+
+logger = logging.get_logger(__name__)
+
+
+class DiaFeatureExtractor(SequenceFeatureExtractor):
+ r"""
+ Constructs an Dia feature extractor.
+
+ This feature extractor inherits from [`~feature_extraction_sequence_utils.SequenceFeatureExtractor`] which contains
+ most of the main methods. Users should refer to this superclass for more information regarding those methods.
+
+ Args:
+ feature_size (`int`, *optional*, defaults to 1):
+ The feature dimension of the extracted features. Use 1 for mono, 2 for stereo.
+ sampling_rate (`int`, *optional*, defaults to 16000):
+ The sampling rate at which the audio waveform should be digitalized, expressed in hertz (Hz).
+ padding_value (`float`, *optional*, defaults to 0.0):
+ The value that is used for padding.
+ hop_length (`int`, *optional*, defaults to 512):
+ Overlap length between successive windows.
+ """
+
+ model_input_names = ["input_values", "n_quantizers"]
+
+ def __init__(
+ self,
+ feature_size: int = 1,
+ sampling_rate: int = 16000,
+ padding_value: float = 0.0,
+ hop_length: int = 512,
+ **kwargs,
+ ):
+ super().__init__(feature_size=feature_size, sampling_rate=sampling_rate, padding_value=padding_value, **kwargs)
+ self.hop_length = hop_length
+
+ def __call__(
+ self,
+ raw_audio: np.ndarray | list[float] | list[np.ndarray] | list[list[float]],
+ padding: bool | str | PaddingStrategy | None = None,
+ truncation: bool | None = False,
+ max_length: int | None = None,
+ return_tensors: str | TensorType | None = None,
+ sampling_rate: int | None = None,
+ ) -> BatchFeature:
+ """
+ Main method to featurize and prepare for the model one or several sequence(s).
+
+ Args:
+ raw_audio (`np.ndarray`, `list[float]`, `list[np.ndarray]`, `list[list[float]]`):
+ The sequence or batch of sequences to be processed. Each sequence can be a numpy array, a list of float
+ values, a list of numpy arrays or a list of list of float values. The numpy array must be of shape
+ `(num_samples,)` for mono audio (`feature_size = 1`), or `(2, num_samples)` for stereo audio
+ (`feature_size = 2`).
+ padding (`bool`, `str` or [`~utils.PaddingStrategy`], *optional*, defaults to `True`):
+ Select a strategy to pad the returned sequences (according to the model's padding side and padding
+ index) among:
+
+ - `True` or `'longest'`: Pad to the longest sequence in the batch (or no padding if only a single
+ sequence if provided).
+ - `'max_length'`: Pad to a maximum length specified with the argument `max_length` or to the maximum
+ acceptable input length for the model if that argument is not provided.
+ - `False` or `'do_not_pad'` (default): No padding (i.e., can output a batch with sequences of different
+ lengths).
+ truncation (`bool`, *optional*, defaults to `False`):
+ Activates truncation to cut input sequences longer than `max_length` to `max_length`.
+ max_length (`int`, *optional*):
+ Maximum length of the returned list and optionally padding length (see above).
+ return_tensors (`str` or [`~utils.TensorType`], *optional*, default to 'pt'):
+ If set, will return tensors instead of list of python integers. Acceptable values are:
+
+ - `'pt'`: Return PyTorch `torch.Tensor` objects.
+ - `'np'`: Return Numpy `np.ndarray` objects.
+ sampling_rate (`int`, *optional*):
+ The sampling rate at which the `audio` input was sampled. It is strongly recommended to pass
+ `sampling_rate` at the forward call to prevent silent errors.
+ """
+ if sampling_rate is not None:
+ if sampling_rate != self.sampling_rate:
+ raise ValueError(
+ f"The model corresponding to this feature extractor: {self} was trained using a sampling rate of"
+ f" {self.sampling_rate}. Please make sure that the provided audio input was sampled with"
+ f" {self.sampling_rate} and not {sampling_rate}."
+ )
+ else:
+ logger.warning(
+ f"It is strongly recommended to pass the `sampling_rate` argument to `{self.__class__.__name__}()`. "
+ "Failing to do so can result in silent errors that might be hard to debug."
+ )
+
+ if padding and truncation:
+ raise ValueError("Both padding and truncation were set. Make sure you only set one.")
+ elif padding is None:
+ # by default let's pad the inputs
+ padding = True
+
+ is_batched = bool(
+ isinstance(raw_audio, (list, tuple)) and (isinstance(raw_audio[0], (np.ndarray, tuple, list)))
+ )
+
+ if is_batched:
+ raw_audio = [np.asarray(audio, dtype=np.float32).T for audio in raw_audio]
+ elif not is_batched and not isinstance(raw_audio, np.ndarray):
+ raw_audio = np.asarray(raw_audio, dtype=np.float32)
+ elif isinstance(raw_audio, np.ndarray) and raw_audio.dtype is np.dtype(np.float64):
+ raw_audio = raw_audio.astype(np.float32)
+
+ # always return batch
+ if not is_batched:
+ raw_audio = [np.asarray(raw_audio).T]
+
+ # convert stereo to mono if necessary, unique to Dia
+ for idx, example in enumerate(raw_audio):
+ if self.feature_size == 2 and example.ndim == 2:
+ raw_audio[idx] = np.mean(example, -1)
+
+ # verify inputs are valid
+ for idx, example in enumerate(raw_audio):
+ if example.ndim > 2:
+ raise ValueError(f"Expected input shape (channels, length) but got shape {example.shape}")
+ if self.feature_size == 1 and example.ndim != 1:
+ raise ValueError(f"Expected mono audio but example has {example.shape[-1]} channels")
+ if self.feature_size == 2 and example.ndim != 1: # note the conversion before
+ raise ValueError(f"Expected stereo audio but example has {example.shape[-1]} channels")
+
+ input_values = BatchFeature({"input_values": raw_audio})
+
+ # temporarily treat it as if we were mono as we also convert stereo to mono
+ original_feature_size = self.feature_size
+ self.feature_size = 1
+
+ # normal padding on batch
+ padded_inputs = self.pad(
+ input_values,
+ max_length=max_length,
+ truncation=truncation,
+ padding=padding,
+ return_attention_mask=True,
+ pad_to_multiple_of=self.hop_length,
+ )
+ padded_inputs["padding_mask"] = padded_inputs.pop("attention_mask")
+
+ input_values = []
+ for example in padded_inputs.pop("input_values"):
+ if self.feature_size == 1:
+ example = example[..., None]
+ input_values.append(example.T)
+
+ padded_inputs["input_values"] = input_values
+ if return_tensors is not None:
+ padded_inputs = padded_inputs.convert_to_tensors(return_tensors)
+
+ # rewrite back to original feature size
+ self.feature_size = original_feature_size
+
+ return padded_inputs
+
+
+__all__ = ["DiaFeatureExtractor"]
diff --git a/third_party/transformers/src/transformers/models/dia/generation_dia.py b/third_party/transformers/src/transformers/models/dia/generation_dia.py
new file mode 100644
index 0000000000000000000000000000000000000000..d22b2fff0d8d29bd10c632b620aa6c07a1fede07
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/dia/generation_dia.py
@@ -0,0 +1,462 @@
+# Copyright 2025 The Nari Labs and HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from collections.abc import Callable
+from typing import Any, Optional
+
+import torch
+import torch.distributed as dist
+
+from ...generation.logits_process import (
+ DiaClassifierFreeGuidanceLogitsProcessor,
+ DiaEOSChannelFilterLogitsProcessor,
+ DiaEOSDelayPatternLogitsProcessor,
+ LogitsProcessorList,
+ TemperatureLogitsWarper,
+)
+from ...generation.stopping_criteria import StoppingCriteriaList
+from ...generation.streamers import BaseStreamer
+from ...generation.utils import GenerateOutput, GenerationConfig, GenerationMixin, GenerationMode
+from ...integrations.deepspeed import is_deepspeed_zero3_enabled
+from ...integrations.fsdp import is_fsdp_managed_module
+from ...modeling_utils import PreTrainedModel
+from ...utils import logging
+
+
+logger = logging.get_logger(__name__)
+
+
+class DiaGenerationMixin(GenerationMixin):
+ # Indicates CFG which needs preparation to be properly handled by repeats
+ _uses_cfg = None
+
+ def _get_logits_processor(
+ self,
+ generation_config: GenerationConfig,
+ input_ids_seq_length: int | None = None,
+ encoder_input_ids: torch.LongTensor | None = None,
+ prefix_allowed_tokens_fn: Callable[[int, torch.Tensor], list[int]] | None = None,
+ logits_processor: LogitsProcessorList | None = None,
+ device: str | None = None,
+ model_kwargs: dict[str, Any] | None = None,
+ negative_prompt_ids: torch.Tensor | None = None,
+ negative_prompt_attention_mask: torch.Tensor | None = None,
+ ) -> LogitsProcessorList:
+ # Need either custom order or custom processor instead
+ # (Temporarily disabling those for the super function)
+ original_guidance_scale = generation_config.guidance_scale
+ original_temperature = generation_config.temperature
+ generation_config.guidance_scale = None
+ generation_config.temperature = None
+
+ # Get base processors and those we can integrate easily
+ custom_processors = LogitsProcessorList()
+
+ if original_temperature is not None and original_temperature != 1.0:
+ custom_processors.append(TemperatureLogitsWarper(original_temperature))
+
+ custom_processors.append(
+ DiaEOSChannelFilterLogitsProcessor(
+ num_channels=len(self.config.delay_pattern),
+ eos_token_id=self.config.decoder_config.eos_token_id,
+ )
+ )
+
+ merged_processors = super()._get_logits_processor(
+ generation_config=generation_config,
+ input_ids_seq_length=input_ids_seq_length,
+ encoder_input_ids=encoder_input_ids,
+ prefix_allowed_tokens_fn=None,
+ logits_processor=custom_processors,
+ device=device,
+ model_kwargs=model_kwargs,
+ negative_prompt_ids=negative_prompt_ids,
+ negative_prompt_attention_mask=negative_prompt_attention_mask,
+ )
+
+ # Custom processors we need at specific positions
+ if original_guidance_scale is not None and original_guidance_scale != 1:
+ cfg_processor = DiaClassifierFreeGuidanceLogitsProcessor(
+ guidance_scale=original_guidance_scale,
+ guidance_top_k=generation_config.top_k,
+ )
+ merged_processors.insert(0, cfg_processor)
+
+ merged_processors.append(
+ DiaEOSDelayPatternLogitsProcessor(
+ delay_pattern=self.config.delay_pattern,
+ eos_token_id=self.config.decoder_config.eos_token_id,
+ max_generation_len=generation_config.max_length,
+ device=device,
+ )
+ )
+
+ # Enable temporarily disabled values back
+ generation_config.guidance_scale = original_guidance_scale
+ generation_config.temperature = original_temperature
+
+ return merged_processors
+
+ def _prepare_generation_config(
+ self, generation_config: GenerationConfig | None, **kwargs: Any
+ ) -> tuple[GenerationConfig, dict]:
+ generation_config, model_kwargs = super()._prepare_generation_config(generation_config, **kwargs)
+
+ if generation_config.temperature is not None and generation_config.temperature < 1.0:
+ logger.warning_once(
+ f"temperature < 1.0 is not supported for Dia; clamping to 1.0 (got {generation_config.temperature})"
+ )
+ generation_config.temperature = 1.0
+ # We allow generation up to max length + max delay pattern
+ # (will revert back to max length after generation)
+ generation_config.max_length += max(self.config.delay_pattern)
+
+ # Internal flag to indicate CFG that needs to prepare unconditioned input
+ self._uses_cfg = generation_config.guidance_scale is not None and generation_config.guidance_scale != 1
+
+ return generation_config, model_kwargs
+
+ def _prepare_model_inputs(
+ self,
+ inputs: torch.Tensor | None = None,
+ bos_token_id: torch.Tensor | None = None,
+ model_kwargs: dict[str, torch.Tensor] | None = None,
+ ) -> tuple[torch.Tensor, str | None, dict[str, torch.Tensor]]:
+ inputs, input_name, model_kwargs = super()._prepare_model_inputs(
+ inputs=inputs,
+ bos_token_id=bos_token_id,
+ model_kwargs=model_kwargs,
+ )
+
+ # If CFG is requested we fill in the unconditioned parts
+ if self._uses_cfg:
+ unconditioned_inputs = torch.zeros_like(inputs)
+ inputs = torch.cat([inputs, unconditioned_inputs], dim=0)
+
+ if model_kwargs.get("attention_mask", None) is not None:
+ model_kwargs["attention_mask"] = model_kwargs["attention_mask"].repeat(2, 1)
+
+ return inputs, input_name, model_kwargs
+
+ def _prepare_decoder_input_ids_for_generation(
+ self,
+ batch_size: int,
+ model_input_name: str,
+ model_kwargs: dict[str, torch.Tensor],
+ decoder_start_token_id: torch.Tensor,
+ device: torch.device | None = None,
+ ) -> tuple[torch.LongTensor, dict[str, torch.Tensor]]:
+ """Prepares `decoder_input_ids` for generation with encoder-decoder models"""
+ # 1. Check whether the user has defined `decoder_input_ids` and `decoder_attention_mask`; if not error out
+ decoder_input_ids = decoder_attention_mask = None
+ if model_kwargs is not None and "decoder_input_ids" in model_kwargs:
+ decoder_input_ids = model_kwargs.pop("decoder_input_ids")
+ if model_kwargs is not None and "decoder_attention_mask" in model_kwargs:
+ decoder_attention_mask = model_kwargs.pop("decoder_attention_mask")
+
+ # We allow generating without preparation (no proper delay) but discourage it
+ if decoder_input_ids is None or decoder_attention_mask is None:
+ logger.warning_once(
+ "In order to generate with Dia, we need the processed audio input: Got `decoder_input_ids`:"
+ f" {decoder_input_ids is not None} and got `decoder_attention_mask`={decoder_attention_mask is not None}."
+ f" This can be achieved via the [`DiaProcessor`] but now defaulting to non-delayed generation."
+ )
+
+ num_channels = self.config.decoder_config.num_channels
+ real_batch_size = batch_size // 2 if self._uses_cfg else batch_size
+
+ if decoder_input_ids is None:
+ decoder_input_ids = torch.full(
+ (real_batch_size, 1, num_channels), decoder_start_token_id, dtype=torch.long, device=device
+ )
+
+ decoder_attention_mask = torch.ones(
+ size=(real_batch_size, decoder_input_ids.shape[1]), dtype=torch.long, device=device
+ )
+
+ # 2. Determine the valid input and what works as mask within the input
+ delay_mask = decoder_input_ids.long()
+ valid_input_size = (
+ decoder_input_ids.shape[1]
+ - (decoder_input_ids[:, :, 0] == self.config.decoder_config.pad_token_id).sum(dim=-1).max()
+ )
+ decoder_input_ids = delay_mask[:, :valid_input_size].transpose(1, 2).long()
+ decoder_attention_mask = decoder_attention_mask[:, :valid_input_size].long()
+
+ # 3. Overwrite into model kwargs
+ model_kwargs["decoder_attention_mask"] = decoder_attention_mask
+ model_kwargs["decoder_delay_mask"] = delay_mask
+
+ return decoder_input_ids, model_kwargs
+
+ def prepare_inputs_for_generation(
+ self,
+ input_ids,
+ encoder_outputs=None, # Using this to easily get the batch size
+ decoder_delay_mask=None,
+ is_first_iteration: bool | None = False,
+ **kwargs,
+ ):
+ # Reshape decoder input_ids to 3D to be compile friendly and to fit the expected model input shape
+ batch_size = encoder_outputs[0].shape[0] // 2 if self._uses_cfg else encoder_outputs[0].shape[0]
+ input_ids = input_ids.reshape(batch_size, self.config.decoder_config.num_channels, -1).transpose(1, 2)
+
+ # Base method handles most things except CFG and the delay pattern mask
+ model_inputs = super().prepare_inputs_for_generation(input_ids, encoder_outputs=encoder_outputs, **kwargs)
+
+ # Post processing for CFG and overwriting via delay pattern mask
+ # 1. Delay pattern mask -- force tokens if not allowed to predict (!= pad_token in mask)
+ model_inputs["decoder_input_ids"] = self.apply_delay_mask(
+ input_ids, self.config.decoder_config.pad_token_id, decoder_delay_mask
+ )
+
+ # Depending on cache usage we need to pass all or just one
+ if model_inputs.get("use_cache", False) and not is_first_iteration:
+ model_inputs["decoder_input_ids"] = model_inputs["decoder_input_ids"][:, -1, :][:, None, :]
+
+ # Be compile friendly
+ model_inputs["decoder_input_ids"] = model_inputs["decoder_input_ids"].contiguous()
+
+ # 2. Apply CFG duplication if needed
+ if self._uses_cfg:
+ for key in ["decoder_input_ids", "decoder_attention_mask", "decoder_position_ids"]:
+ if model_inputs.get(key, None) is not None:
+ # double first dimension and keep everything else the same
+ repeat_pattern = tuple([2] + [1] * (model_inputs[key].ndim - 1))
+ model_inputs[key] = model_inputs[key].repeat(*repeat_pattern)
+
+ return model_inputs
+
+ @staticmethod
+ def apply_delay_mask(input_ids: torch.Tensor, pad_id: int, delay_mask: torch.Tensor | None) -> torch.Tensor:
+ if delay_mask is None:
+ return input_ids
+
+ mask_len = min(input_ids.shape[1], delay_mask.shape[1])
+ valid_mask = delay_mask[:, :mask_len, :]
+ valid_input = input_ids[:, :mask_len, :]
+
+ # Overwrite the respective parts of the input
+ input_ids[:, :mask_len, :] = torch.where(valid_mask == pad_id, valid_input, valid_mask)
+
+ return input_ids
+
+ def _main_generate_loop(
+ self,
+ inputs: torch.Tensor | None = None,
+ generation_config: GenerationConfig | None = None,
+ logits_processor: LogitsProcessorList | None = None,
+ stopping_criteria: StoppingCriteriaList | None = None,
+ prefix_allowed_tokens_fn: Callable[[int, torch.Tensor], list[int]] | None = None,
+ synced_gpus: bool | None = None,
+ assistant_model: Optional["PreTrainedModel"] = None,
+ streamer: Optional["BaseStreamer"] = None,
+ negative_prompt_ids: torch.Tensor | None = None,
+ negative_prompt_attention_mask: torch.Tensor | None = None,
+ custom_generate: str | None = None,
+ **kwargs,
+ ):
+ # ********** mostly taken from main generate function up to calling the different methods (see NOTE) **********
+ # 1. Handle `generation_config` and kwargs that might update it, and validate the `.generate()` call
+ generation_mode_kwargs = self._extract_generation_mode_kwargs(
+ custom_generate,
+ kwargs,
+ synced_gpus,
+ assistant_model,
+ streamer,
+ )
+ generation_config, model_kwargs = self._prepare_generation_config(generation_config, **kwargs)
+ generation_mode = generation_config.get_generation_mode(assistant_model)
+
+ if generation_mode not in (GenerationMode.SAMPLE, GenerationMode.GREEDY_SEARCH):
+ raise ValueError(
+ "Got incompatible mode for generation, should be one of greedy or sampling. "
+ "Ensure that beam search is de-activated by setting `num_beams=1`."
+ )
+
+ self._validate_model_kwargs(model_kwargs.copy())
+ self._validate_generation_mode(generation_mode, generation_config, generation_mode_kwargs)
+
+ # 2. Set generation parameters if not already defined
+ if synced_gpus is None:
+ synced_gpus = (is_deepspeed_zero3_enabled() or is_fsdp_managed_module(self)) and dist.get_world_size() > 1
+
+ logits_processor = logits_processor if logits_processor is not None else LogitsProcessorList()
+ stopping_criteria = stopping_criteria if stopping_criteria is not None else StoppingCriteriaList()
+
+ # 3. Define model inputs
+ kwargs_has_attention_mask = model_kwargs.get("attention_mask", None) is not None
+ inputs_tensor, model_input_name, model_kwargs = self._prepare_model_inputs(
+ inputs, generation_config.bos_token_id, model_kwargs
+ )
+ batch_size = inputs_tensor.shape[0]
+
+ device = inputs_tensor.device
+ self._prepare_special_tokens(generation_config, kwargs_has_attention_mask, device=device)
+
+ # 4. Define other model kwargs
+ if "encoder_outputs" not in model_kwargs:
+ # if model is encoder decoder encoder_outputs are created and added to `model_kwargs`
+ model_kwargs = self._prepare_encoder_decoder_kwargs_for_generation(
+ inputs_tensor, model_kwargs, model_input_name, generation_config
+ )
+
+ # 5. Prepare `input_ids` which will be used for auto-regressive generation
+ input_ids, model_kwargs = self._prepare_decoder_input_ids_for_generation(
+ batch_size=batch_size,
+ model_input_name=model_input_name,
+ model_kwargs=model_kwargs,
+ decoder_start_token_id=generation_config._decoder_start_token_tensor,
+ device=inputs_tensor.device,
+ )
+
+ if generation_config.token_healing:
+ input_ids = self.heal_tokens(input_ids, generation_mode_kwargs.get("tokenizer"))
+
+ if streamer is not None:
+ streamer.put(input_ids.cpu())
+
+ # 6. Prepare `max_length` depending on other stopping criteria.
+ # NOTE: incorrect `input_ids.shape[1]` previously
+ input_ids_length = input_ids.shape[-1]
+ has_default_max_length = kwargs.get("max_length") is None and generation_config.max_length is not None
+ has_default_min_length = kwargs.get("min_length") is None and generation_config.min_length is not None
+ generation_config = self._prepare_generated_length(
+ generation_config=generation_config,
+ has_default_max_length=has_default_max_length,
+ has_default_min_length=has_default_min_length,
+ model_input_name=model_input_name,
+ inputs_tensor=inputs_tensor,
+ input_ids_length=input_ids_length,
+ )
+
+ # If the model supports `logits_to_keep` in forward(), set it to 1 to avoid computing the whole
+ # logit matrix. This can save a lot of memory during the first forward pass. Note that assisted decoding
+ # dynamically overrides this value as it can need more than the last token logits
+ if self._supports_logits_to_keep() and "logits_to_keep" not in model_kwargs:
+ model_kwargs["logits_to_keep"] = 1
+
+ self._validate_generated_length(generation_config, input_ids_length, has_default_max_length)
+
+ # 7. Prepare the cache.
+ # - `model_kwargs` may be updated in place with a cache as defined by the parameters in `generation_config`.
+ # - different models have a different cache name expected by the model (default = "past_key_values")
+ # - `max_length`, prepared above, is used to determine the maximum cache length
+ max_cache_length = generation_config.max_length - 1
+ if (
+ inputs_tensor.shape[1] != input_ids_length
+ and model_input_name == "inputs_embeds"
+ and not self.config.is_encoder_decoder
+ ):
+ max_cache_length += inputs_tensor.shape[1]
+ self._prepare_cache_for_generation(
+ generation_config, model_kwargs, generation_mode, batch_size, max_cache_length
+ )
+
+ # 8. prepare logits processors and stopping criteria
+ prepared_logits_processor = self._get_logits_processor(
+ generation_config=generation_config,
+ input_ids_seq_length=input_ids_length,
+ encoder_input_ids=inputs_tensor,
+ prefix_allowed_tokens_fn=prefix_allowed_tokens_fn,
+ logits_processor=logits_processor,
+ device=inputs_tensor.device,
+ model_kwargs=model_kwargs,
+ negative_prompt_ids=negative_prompt_ids,
+ negative_prompt_attention_mask=negative_prompt_attention_mask,
+ )
+ prepared_stopping_criteria = self._get_stopping_criteria(
+ generation_config=generation_config,
+ stopping_criteria=stopping_criteria,
+ tokenizer=generation_mode_kwargs.get("tokenizer"),
+ )
+
+ # Set model_kwargs `use_cache` so we can use it later in forward runs
+ model_kwargs["use_cache"] = generation_config.use_cache
+ # ******************* taken from main generate function up to calling the different methods *******************
+
+ # Prepare inner 2D logic in generation loop
+ input_ids = input_ids.reshape(-1, input_ids.shape[-1])
+
+ # 10. expand input_ids with `num_return_sequences` additional sequences per batch
+ if generation_config.num_return_sequences > 1:
+ raise ValueError("`num_return_sequences>1` is incompatible with Dia.")
+
+ # 11. run sample (it degenerates to greedy search when `generation_config.do_sample=False`)
+ return self._sample(
+ input_ids,
+ logits_processor=prepared_logits_processor,
+ stopping_criteria=prepared_stopping_criteria,
+ generation_config=generation_config,
+ **generation_mode_kwargs,
+ **model_kwargs,
+ )
+
+ @torch.no_grad()
+ def generate(
+ self,
+ inputs: torch.Tensor | None = None,
+ generation_config: GenerationConfig | None = None,
+ logits_processor: LogitsProcessorList | None = None,
+ stopping_criteria: StoppingCriteriaList | None = None,
+ prefix_allowed_tokens_fn: Callable[[int, torch.Tensor], list[int]] | None = None,
+ synced_gpus: bool | None = None,
+ assistant_model: Optional["PreTrainedModel"] = None,
+ streamer: Optional["BaseStreamer"] = None,
+ negative_prompt_ids: torch.Tensor | None = None,
+ negative_prompt_attention_mask: torch.Tensor | None = None,
+ custom_generate: str | None = None,
+ **kwargs,
+ ) -> GenerateOutput | torch.LongTensor:
+ # We expect the initial input ids to be the complete mask (delayed input)
+ delay_mask = kwargs.get("decoder_input_ids")
+ if delay_mask is not None:
+ delay_mask = delay_mask.clone()
+
+ output = self._main_generate_loop(
+ inputs=inputs,
+ generation_config=generation_config,
+ logits_processor=logits_processor,
+ stopping_criteria=stopping_criteria,
+ prefix_allowed_tokens_fn=prefix_allowed_tokens_fn,
+ synced_gpus=synced_gpus,
+ assistant_model=assistant_model,
+ streamer=streamer,
+ negative_prompt_ids=negative_prompt_ids,
+ negative_prompt_attention_mask=negative_prompt_attention_mask,
+ custom_generate=custom_generate,
+ **kwargs,
+ )
+
+ return_dict_in_generate = not isinstance(output, torch.Tensor)
+
+ if return_dict_in_generate:
+ output_sequences = output.sequences
+ else:
+ output_sequences = output
+
+ # Reshape from 2D (bsz * channels, seq_len) to 3D (bsz, seq_len, channels)
+ num_channels = self.config.decoder_config.num_channels
+ bsz = output_sequences.shape[0] // num_channels
+ output_sequences = output_sequences.reshape(bsz, num_channels, -1).transpose(1, 2)
+
+ # Apply delay mask
+ output_sequences = self.apply_delay_mask(output_sequences, self.config.decoder_config.pad_token_id, delay_mask)
+
+ if return_dict_in_generate:
+ output.sequences = output_sequences
+ else:
+ output = output_sequences
+
+ return output
diff --git a/third_party/transformers/src/transformers/models/dia/modeling_dia.py b/third_party/transformers/src/transformers/models/dia/modeling_dia.py
new file mode 100644
index 0000000000000000000000000000000000000000..629dfd4cdb35395e18d11cf5b4483ec0673fcf9c
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/dia/modeling_dia.py
@@ -0,0 +1,865 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/dia/modular_dia.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_dia.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2025 The Nari Labs and HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from collections.abc import Callable
+from typing import Optional
+
+import torch
+from torch import nn
+
+from ... import initialization as init
+from ...activations import ACT2FN
+from ...cache_utils import Cache, DynamicCache, EncoderDecoderCache
+from ...integrations import use_kernel_forward_from_hub, use_kernel_func_from_hub, use_kernelized_func
+from ...masking_utils import create_bidirectional_mask, create_causal_mask
+from ...modeling_flash_attention_utils import FlashAttentionKwargs
+from ...modeling_layers import GradientCheckpointingLayer
+from ...modeling_outputs import (
+ BaseModelOutput,
+ BaseModelOutputWithPastAndCrossAttentions,
+ Seq2SeqLMOutput,
+ Seq2SeqModelOutput,
+)
+from ...modeling_rope_utils import ROPE_INIT_FUNCTIONS, dynamic_rope_update
+from ...modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
+from ...processing_utils import Unpack
+from ...utils import TransformersKwargs, auto_docstring, can_return_tuple, is_torchdynamo_compiling, logging
+from ...utils.generic import maybe_autocast, merge_with_config_defaults
+from ...utils.output_capturing import capture_outputs
+from .configuration_dia import DiaConfig, DiaDecoderConfig, DiaEncoderConfig
+from .generation_dia import DiaGenerationMixin
+
+
+logger = logging.get_logger(__name__)
+
+
+@auto_docstring
+class DiaPreTrainedModel(PreTrainedModel):
+ config: DiaConfig
+ base_model_prefix = "model"
+ supports_gradient_checkpointing = True
+ _supports_flash_attn = True
+ _supports_sdpa = True
+ _supports_flex_attn = True
+ _can_compile_fullgraph = True
+ main_input_name = "input_ids"
+ _no_split_modules = ["DiaEncoderLayer", "DiaDecoderLayer"]
+
+ def _init_weights(self, module):
+ super()._init_weights(module)
+ if isinstance(module, DiaMultiChannelEmbedding):
+ offsets = torch.arange(self.config.num_channels, dtype=torch.long) * self.config.vocab_size
+ init.copy_(module.offsets, offsets)
+
+
+class DiaMultiChannelEmbedding(nn.Module):
+ """In order to efficiently compute the audio embedding from the 9 different channels,
+ we vectorize the embedding process by using a single embedding layer and an offset.
+ Example:
+ - num_embeds = 4
+ - vocab_size = 8
+ - num_channels = 3
+ We would have offsets = [0, 8, 16]
+ If audio_codes = [0, 1, 2, 3], [1, 3, 4, 7], [5, 6, 7, 8],
+ then tokens = audio_codes + offsets
+ = [0, 1, 2, 3, 9, 11, 12, 15, 21, 22, 23, 24]
+ This allows us to use a single embedding layer for all channels.
+ """
+
+ def __init__(self, config: DiaDecoderConfig):
+ super().__init__()
+ self.embed = nn.Embedding(config.vocab_size * config.num_channels, config.hidden_size)
+ self.hidden_size = config.hidden_size
+ self.num_channels = config.num_channels
+ offsets = torch.arange(config.num_channels, dtype=torch.long) * config.vocab_size # (C,)
+ self.register_buffer("offsets", offsets, persistent=False)
+
+ def forward(self, audio_codes: torch.Tensor) -> torch.Tensor:
+ tokens = (audio_codes + self.offsets.to(audio_codes.device)).squeeze(1)
+ embeds = self.embed(tokens).view(tokens.shape[0], audio_codes.shape[1], -1, self.hidden_size)
+ return embeds.sum(dim=2)
+
+
+class DiaMLP(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+
+ self.config = config
+ self.gate_up_proj = nn.Linear(config.hidden_size, 2 * config.intermediate_size, bias=False)
+ self.down_proj = nn.Linear(config.intermediate_size, config.hidden_size, bias=False)
+ self.activation_fn = ACT2FN[config.hidden_act]
+
+ def forward(self, hidden_states: torch.FloatTensor) -> torch.FloatTensor:
+ up_states = self.gate_up_proj(hidden_states)
+
+ gate, up_states = up_states.chunk(2, dim=-1)
+ up_states = up_states * self.activation_fn(gate)
+
+ return self.down_proj(up_states)
+
+
+@use_kernel_forward_from_hub("RMSNorm")
+class DiaRMSNorm(nn.Module):
+ def __init__(self, hidden_size, eps: float = 1e-6) -> None:
+ """
+ DiaRMSNorm is equivalent to T5LayerNorm
+ """
+ super().__init__()
+ self.weight = nn.Parameter(torch.ones(hidden_size))
+ self.variance_epsilon = eps
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ input_dtype = hidden_states.dtype
+ hidden_states = hidden_states.to(torch.float32)
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
+ hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
+ return self.weight * hidden_states.to(input_dtype)
+
+ def extra_repr(self):
+ return f"{tuple(self.weight.shape)}, eps={self.variance_epsilon}"
+
+
+class DiaRotaryEmbedding(nn.Module):
+ inv_freq: torch.Tensor # fix linting for `register_buffer`
+
+ def __init__(self, config: DiaConfig, device=None):
+ super().__init__()
+ self.max_seq_len_cached = config.max_position_embeddings
+ self.original_max_seq_len = config.max_position_embeddings
+
+ self.config = config
+
+ self.rope_type = self.config.rope_parameters["rope_type"]
+ rope_init_fn: Callable = self.compute_default_rope_parameters
+ if self.rope_type != "default":
+ rope_init_fn = ROPE_INIT_FUNCTIONS[self.rope_type]
+ inv_freq, self.attention_scaling = rope_init_fn(self.config, device)
+
+ self.register_buffer("inv_freq", inv_freq, persistent=False)
+ self.register_buffer("original_inv_freq", inv_freq.clone(), persistent=False)
+
+ @staticmethod
+ def compute_default_rope_parameters(
+ config: DiaConfig | None = None,
+ device: Optional["torch.device"] = None,
+ seq_len: int | None = None,
+ ) -> tuple["torch.Tensor", float]:
+ """
+ Computes the inverse frequencies according to the original RoPE implementation
+ Args:
+ config ([`~transformers.PreTrainedConfig`]):
+ The model configuration.
+ device (`torch.device`):
+ The device to use for initialization of the inverse frequencies.
+ seq_len (`int`, *optional*):
+ The current sequence length. Unused for this type of RoPE.
+ Returns:
+ Tuple of (`torch.Tensor`, `float`), containing the inverse frequencies for the RoPE embeddings and the
+ post-processing scaling factor applied to the computed cos/sin (unused in this type of RoPE).
+ """
+ base = config.rope_parameters["rope_theta"]
+ dim = getattr(config, "head_dim", None) or config.hidden_size // config.num_attention_heads
+
+ attention_factor = 1.0 # Unused in this type of RoPE
+
+ # Compute the inverse frequencies
+ inv_freq = 1.0 / (
+ base ** (torch.arange(0, dim, 2, dtype=torch.int64).to(device=device, dtype=torch.float) / dim)
+ )
+ return inv_freq, attention_factor
+
+ @torch.no_grad()
+ @dynamic_rope_update # power user: used with advanced RoPE types (e.g. dynamic rope)
+ def forward(self, x, position_ids):
+ inv_freq_expanded = self.inv_freq[None, :, None].float().expand(position_ids.shape[0], -1, 1).to(x.device)
+ position_ids_expanded = position_ids[:, None, :].float()
+
+ device_type = x.device.type if isinstance(x.device.type, str) and x.device.type != "mps" else "cpu"
+ with maybe_autocast(device_type=device_type, enabled=False): # Force float32
+ freqs = (inv_freq_expanded.float() @ position_ids_expanded.float()).transpose(1, 2)
+ emb = torch.cat((freqs, freqs), dim=-1)
+ cos = emb.cos() * self.attention_scaling
+ sin = emb.sin() * self.attention_scaling
+
+ return cos.to(dtype=x.dtype), sin.to(dtype=x.dtype)
+
+
+def rotate_half(x):
+ """Rotates half the hidden dims of the input."""
+ x1 = x[..., : x.shape[-1] // 2]
+ x2 = x[..., x.shape[-1] // 2 :]
+ return torch.cat((-x2, x1), dim=-1)
+
+
+@use_kernel_func_from_hub("rotary_pos_emb")
+def apply_rotary_pos_emb(q, k, cos, sin, unsqueeze_dim=1):
+ """Applies Rotary Position Embedding to the query and key tensors.
+
+ Args:
+ q (`torch.Tensor`): The query tensor.
+ k (`torch.Tensor`): The key tensor.
+ cos (`torch.Tensor`): The cosine part of the rotary embedding.
+ sin (`torch.Tensor`): The sine part of the rotary embedding.
+ unsqueeze_dim (`int`, *optional*, defaults to 1):
+ The 'unsqueeze_dim' argument specifies the dimension along which to unsqueeze cos[position_ids] and
+ sin[position_ids] so that they can be properly broadcasted to the dimensions of q and k. For example, note
+ that cos[position_ids] and sin[position_ids] have the shape [batch_size, seq_len, head_dim]. Then, if q and
+ k have the shape [batch_size, heads, seq_len, head_dim], then setting unsqueeze_dim=1 makes
+ cos[position_ids] and sin[position_ids] broadcastable to the shapes of q and k. Similarly, if q and k have
+ the shape [batch_size, seq_len, heads, head_dim], then set unsqueeze_dim=2.
+ Returns:
+ `tuple(torch.Tensor)` comprising of the query and key tensors rotated using the Rotary Position Embedding.
+ """
+ cos = cos.unsqueeze(unsqueeze_dim)
+ sin = sin.unsqueeze(unsqueeze_dim)
+ q_embed = (q * cos) + (rotate_half(q) * sin)
+ k_embed = (k * cos) + (rotate_half(k) * sin)
+ return q_embed, k_embed
+
+
+def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
+ """
+ This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
+ num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
+ """
+ batch, num_key_value_heads, slen, head_dim = hidden_states.shape
+ if n_rep == 1:
+ return hidden_states
+ hidden_states = hidden_states[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim)
+ return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
+
+
+def eager_attention_forward(
+ module: nn.Module,
+ query: torch.Tensor,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ attention_mask: torch.Tensor | None,
+ scaling: float,
+ dropout: float = 0.0,
+ **kwargs: Unpack[TransformersKwargs],
+):
+ key_states = repeat_kv(key, module.num_key_value_groups)
+ value_states = repeat_kv(value, module.num_key_value_groups)
+
+ attn_weights = torch.matmul(query, key_states.transpose(2, 3)) * scaling
+ if attention_mask is not None:
+ attn_weights = attn_weights + attention_mask
+
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query.dtype)
+ attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
+ attn_output = torch.matmul(attn_weights, value_states)
+ attn_output = attn_output.transpose(1, 2).contiguous()
+
+ return attn_output, attn_weights
+
+
+@use_kernelized_func(apply_rotary_pos_emb)
+class DiaSelfAttention(nn.Module):
+ """Multi-headed attention from 'Attention Is All You Need' paper"""
+
+ def __init__(self, config: DiaEncoderConfig | DiaDecoderConfig, layer_idx: int, is_causal: bool = False):
+ super().__init__()
+ self.config = config
+ self.layer_idx = layer_idx
+ self.hidden_size = config.hidden_size
+ self.num_heads = self.config.num_attention_heads
+ self.num_key_value_heads = self.config.num_key_value_heads or self.num_heads
+ self.num_key_value_groups = self.num_heads // self.num_key_value_heads
+ self.head_dim = getattr(config, "head_dim", config.hidden_size // self.num_heads)
+ self.scaling = 1
+ self.attention_dropout = 0.0
+ self.is_causal = is_causal
+
+ self.q_proj = nn.Linear(self.hidden_size, self.num_heads * self.head_dim, bias=False)
+ self.k_proj = nn.Linear(self.hidden_size, self.num_key_value_heads * self.head_dim, bias=False)
+ self.v_proj = nn.Linear(self.hidden_size, self.num_key_value_heads * self.head_dim, bias=False)
+ self.o_proj = nn.Linear(self.num_heads * self.head_dim, self.hidden_size, bias=False)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
+ attention_mask: torch.Tensor | None = None,
+ past_key_values: Cache | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple[torch.Tensor, torch.Tensor]:
+ input_shape = hidden_states.shape[:-1]
+ hidden_shape = (*input_shape, -1, self.head_dim)
+
+ query_states = self.q_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+ key_states = self.k_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+ value_states = self.v_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+
+ cos, sin = position_embeddings
+ query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin)
+
+ if past_key_values is not None:
+ key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx)
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ query_states,
+ key_states,
+ value_states,
+ attention_mask,
+ dropout=0.0 if not self.training else self.attention_dropout,
+ scaling=self.scaling,
+ **kwargs,
+ )
+
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
+ attn_output = self.o_proj(attn_output)
+ return attn_output, attn_weights
+
+
+class DiaCrossAttention(nn.Module):
+ """Multi-headed attention from 'Attention Is All You Need' paper"""
+
+ def __init__(self, config: DiaDecoderConfig, layer_idx: int):
+ super().__init__()
+ self.config = config
+ self.layer_idx = layer_idx
+ self.hidden_size = config.hidden_size
+ self.cross_hidden_size = config.cross_hidden_size
+ self.num_heads = self.config.cross_num_attention_heads
+ self.num_key_value_heads = self.config.cross_num_key_value_heads
+ self.num_key_value_groups = self.num_heads // self.num_key_value_heads
+ self.head_dim = config.cross_head_dim
+ self.scaling = 1
+ self.attention_dropout = 0.0
+ self.is_causal = False
+
+ self.q_proj = nn.Linear(self.hidden_size, self.num_heads * self.head_dim, bias=False)
+ self.k_proj = nn.Linear(self.cross_hidden_size, self.num_key_value_heads * self.head_dim, bias=False)
+ self.v_proj = nn.Linear(self.cross_hidden_size, self.num_key_value_heads * self.head_dim, bias=False)
+ self.o_proj = nn.Linear(self.num_heads * self.head_dim, self.hidden_size, bias=False)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ cross_attention_states: torch.Tensor,
+ attention_mask: torch.Tensor | None = None,
+ past_key_values: EncoderDecoderCache | None = None,
+ **kwargs: Unpack[FlashAttentionKwargs],
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
+ input_shape = hidden_states.shape[:-1]
+ hidden_shape = (*input_shape, -1, self.head_dim)
+ cross_shape = (*cross_attention_states.shape[:-1], -1, self.head_dim)
+
+ query_states = self.q_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+
+ is_updated = past_key_values.is_updated.get(self.layer_idx) if past_key_values is not None else False
+ if past_key_values is not None and is_updated:
+ # reuse k,v, cross_attentions
+ key_states = past_key_values.cross_attention_cache.layers[self.layer_idx].keys
+ value_states = past_key_values.cross_attention_cache.layers[self.layer_idx].values
+ else:
+ key_states = self.k_proj(cross_attention_states).view(cross_shape).transpose(1, 2)
+ value_states = self.v_proj(cross_attention_states).view(cross_shape).transpose(1, 2)
+
+ if past_key_values is not None:
+ # save all states to the cache
+ key_states, value_states = past_key_values.cross_attention_cache.update(
+ key_states,
+ value_states,
+ self.layer_idx,
+ )
+ # set flag that curr layer for cross-attn is already updated so we can re-use in subsequent calls
+ past_key_values.is_updated[self.layer_idx] = True
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ query_states,
+ key_states,
+ value_states,
+ attention_mask,
+ scaling=self.scaling,
+ **kwargs,
+ )
+
+ attn_output = attn_output.reshape((*input_shape, -1)).contiguous()
+ attn_output = self.o_proj(attn_output)
+ return attn_output, attn_weights
+
+
+class DiaEncoderLayer(GradientCheckpointingLayer):
+ def __init__(self, config: DiaEncoderConfig, layer_idx: int):
+ super().__init__()
+ self.pre_sa_norm = DiaRMSNorm(config.hidden_size, eps=config.norm_eps)
+ self.self_attention = DiaSelfAttention(config, layer_idx, is_causal=False)
+ self.post_sa_norm = DiaRMSNorm(config.hidden_size, eps=config.norm_eps)
+ self.mlp = DiaMLP(config)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
+ attention_mask: torch.Tensor | None = None,
+ **kwargs: Unpack[FlashAttentionKwargs],
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
+ residual = hidden_states
+ normed_states = self.pre_sa_norm(hidden_states)
+ self_attn_output, _ = self.self_attention(
+ normed_states,
+ position_embeddings=position_embeddings,
+ attention_mask=attention_mask,
+ **kwargs,
+ )
+ hidden_states = residual + self_attn_output
+
+ residual = hidden_states
+ normed_states = self.post_sa_norm(hidden_states)
+ mlp_out = self.mlp(normed_states)
+ hidden_states = residual + mlp_out
+
+ return hidden_states
+
+
+class DiaEncoder(DiaPreTrainedModel):
+ _can_record_outputs = {
+ "hidden_states": DiaEncoderLayer,
+ "attentions": DiaSelfAttention,
+ }
+
+ def __init__(self, config: DiaEncoderConfig):
+ super().__init__(config)
+ self.config = config
+
+ self.embedding = nn.Embedding(config.vocab_size, config.hidden_size)
+ self.layers = nn.ModuleList(
+ [DiaEncoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
+ )
+ self.norm = DiaRMSNorm(config.hidden_size, eps=config.norm_eps)
+ self.rotary_emb = DiaRotaryEmbedding(config=config)
+
+ self.post_init()
+
+ @merge_with_config_defaults
+ @capture_outputs
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.Tensor,
+ attention_mask: torch.Tensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> BaseModelOutput:
+ hidden_states = self.embedding(input_ids)
+
+ # RoPE
+ # Note: We expect right padding and hence always generate
+ # the position ids on the fly to reduce preparation overhead
+ position_ids = torch.arange(input_ids.shape[-1], device=input_ids.device)[None, :]
+
+ attention_mask = create_bidirectional_mask(
+ config=self.config,
+ inputs_embeds=hidden_states,
+ attention_mask=attention_mask,
+ )
+ position_embeddings = self.rotary_emb(hidden_states, position_ids=position_ids)
+
+ for encoder_layer in self.layers:
+ hidden_states = encoder_layer(
+ hidden_states,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ position_embeddings=position_embeddings,
+ **kwargs,
+ )
+
+ hidden_states = self.norm(hidden_states)
+
+ return BaseModelOutput(last_hidden_state=hidden_states)
+
+
+class DiaDecoderLayer(GradientCheckpointingLayer):
+ def __init__(self, config: DiaDecoderConfig, layer_idx: int):
+ super().__init__()
+ self.embed_dim = config.hidden_size
+ self.self_attention = DiaSelfAttention(config, layer_idx, is_causal=True)
+ self.cross_attention = DiaCrossAttention(config, layer_idx)
+ self.pre_sa_norm = DiaRMSNorm(config.hidden_size, eps=config.norm_eps)
+ self.pre_ca_norm = DiaRMSNorm(config.hidden_size, eps=config.norm_eps)
+ self.pre_mlp_norm = DiaRMSNorm(config.hidden_size, eps=config.norm_eps)
+ self.mlp = DiaMLP(config)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
+ attention_mask: torch.Tensor | None = None,
+ encoder_hidden_states: torch.Tensor | None = None,
+ encoder_attention_mask: torch.Tensor | None = None,
+ past_key_values: EncoderDecoderCache | None = None,
+ **kwargs,
+ ) -> tuple[torch.Tensor, torch.Tensor | None, torch.Tensor | None]:
+ self_attn_cache = past_key_values
+ if isinstance(self_attn_cache, EncoderDecoderCache):
+ self_attn_cache = self_attn_cache.self_attention_cache
+
+ residual = hidden_states
+ normed_states = self.pre_sa_norm(hidden_states)
+ self_attn_output, _ = self.self_attention(
+ normed_states,
+ position_embeddings,
+ attention_mask,
+ # Needs to be an arg in order to function properly
+ # on inplace operations to be carried (e.g. compile)
+ self_attn_cache,
+ **kwargs,
+ )
+ hidden_states = residual + self_attn_output
+
+ residual = hidden_states
+ normed_states = self.pre_ca_norm(hidden_states)
+ cross_states, _ = self.cross_attention(
+ normed_states,
+ encoder_hidden_states,
+ attention_mask=encoder_attention_mask,
+ past_key_values=past_key_values,
+ **kwargs,
+ )
+ hidden_states = residual + cross_states
+
+ residual = hidden_states
+ normed_states = self.pre_mlp_norm(hidden_states)
+ mlp_out = self.mlp(normed_states)
+ hidden_states = residual + mlp_out
+
+ return hidden_states
+
+
+class DiaDecoder(DiaPreTrainedModel):
+ """Transformer Decoder Stack using DenseGeneral."""
+
+ _can_record_outputs = {
+ "hidden_states": DiaDecoderLayer,
+ "attentions": [DiaSelfAttention, DiaCrossAttention],
+ }
+
+ def __init__(self, config: DiaDecoderConfig):
+ super().__init__(config)
+ self.num_channels = config.num_channels
+ self.vocab_size = config.vocab_size
+ self.embeddings = DiaMultiChannelEmbedding(config)
+ self.layers = nn.ModuleList(
+ [DiaDecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
+ )
+ self.norm = DiaRMSNorm(config.hidden_size, eps=config.norm_eps)
+ self.rotary_emb = DiaRotaryEmbedding(config=config)
+
+ self.post_init()
+
+ @merge_with_config_defaults
+ @capture_outputs
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.Tensor,
+ position_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ encoder_hidden_states: torch.FloatTensor | None = None,
+ encoder_attention_mask: torch.LongTensor | None = None,
+ past_key_values: EncoderDecoderCache | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> BaseModelOutputWithPastAndCrossAttentions | tuple:
+ r"""
+ input_ids (`torch.LongTensor` of shape `(batch_size, sequence_length, num_codebooks)`):
+ The original `decoder_input_ids` in 3D shape to facilitate more efficient computations.
+
+ [What are input IDs?](../glossary#input-ids)
+ """
+
+ batch_size, seq_length = input_ids.size()[:-1]
+ past_key_values_length = past_key_values.get_seq_length() if past_key_values is not None else 0
+
+ if position_ids is None:
+ position_ids = torch.arange(seq_length, device=input_ids.device) + past_key_values_length
+ position_ids = position_ids.unsqueeze(0)
+
+ # RoPE
+ hidden_states = self.embeddings(input_ids)
+
+ if attention_mask is None and not is_torchdynamo_compiling():
+ # required mask seq length can be calculated via length of past cache
+ mask_seq_length = past_key_values_length + seq_length
+ attention_mask = torch.ones(batch_size, mask_seq_length, device=input_ids.device)
+
+ attention_mask = create_causal_mask(
+ config=self.config,
+ inputs_embeds=hidden_states,
+ attention_mask=attention_mask,
+ past_key_values=past_key_values,
+ )
+ encoder_attention_mask = create_bidirectional_mask(
+ config=self.config,
+ inputs_embeds=hidden_states,
+ attention_mask=encoder_attention_mask,
+ encoder_hidden_states=encoder_hidden_states,
+ )
+ position_embeddings = self.rotary_emb(hidden_states, position_ids=position_ids)
+
+ for layer in self.layers:
+ hidden_states = layer(
+ hidden_states,
+ # Needs to be an arg in order to function properly
+ # on inplace operations to be carried (e.g. compile)
+ position_embeddings,
+ attention_mask,
+ encoder_hidden_states,
+ encoder_attention_mask=encoder_attention_mask,
+ past_key_values=past_key_values,
+ position_ids=position_ids,
+ **kwargs,
+ )
+
+ hidden_states = self.norm(hidden_states)
+
+ return BaseModelOutputWithPastAndCrossAttentions(
+ last_hidden_state=hidden_states,
+ past_key_values=past_key_values,
+ )
+
+
+@auto_docstring(
+ custom_intro="""
+ The bare Dia model outputting raw hidden-states without any specific head on top.
+ """
+)
+class DiaModel(DiaPreTrainedModel):
+ def __init__(self, config: DiaConfig):
+ super().__init__(config)
+ self.config = config
+ self.encoder = DiaEncoder(config.encoder_config)
+ self.decoder = DiaDecoder(config.decoder_config)
+ self.post_init()
+
+ @auto_docstring
+ @can_return_tuple
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.LongTensor | None = None,
+ decoder_input_ids: torch.LongTensor | None = None,
+ decoder_position_ids: torch.LongTensor | None = None,
+ decoder_attention_mask: torch.LongTensor | None = None,
+ encoder_outputs: BaseModelOutput | tuple | None = None,
+ past_key_values: EncoderDecoderCache | None = None,
+ use_cache: bool | None = None,
+ **kwargs,
+ ) -> tuple | Seq2SeqModelOutput:
+ r"""
+ decoder_input_ids (`torch.LongTensor` of shape `(batch_size * num_codebooks, target_sequence_length)
+ or (batch_size, target_sequence_length, num_codebooks)`, *optional*):
+ 1. (batch_size * num_codebooks, target_sequence_length): corresponds to the general use case where
+ the audio input codebooks are flattened into the batch dimension. This also aligns with the flat-
+ tened audio logits which are used to calculate the loss.
+
+ 2. (batch_size, sequence_length, num_codebooks): corresponds to the internally used shape of
+ Dia to calculate embeddings and subsequent steps more efficiently.
+
+ If no `decoder_input_ids` are provided, it will create a tensor of `bos_token_id` with shape
+ `(batch_size, 1, num_codebooks)`. Indices can be obtained using the [`DiaProcessor`]. See
+ [`DiaProcessor.__call__`] for more details.
+
+ [What are decoder input IDs?](../glossary#decoder-input-ids)
+ decoder_position_ids (`torch.LongTensor` of shape `(batch_size, target_sequence_length)`):
+ Indices of positions of each input sequence tokens in the position embeddings.
+ Used to calculate the position embeddings up to `config.decoder_config.max_position_embeddings`.
+
+ [What are position IDs?](../glossary#position-ids)
+ """
+
+ if input_ids is None and encoder_outputs is None:
+ raise ValueError(
+ "You should either provide text ids or the cached text encodings. Neither has been found."
+ )
+
+ if self.is_gradient_checkpointing and self.training:
+ if use_cache:
+ logger.warning_once(
+ "`use_cache=True` is incompatible with gradient checkpointing. Setting `use_cache=False`..."
+ )
+ use_cache = False
+
+ if use_cache and past_key_values is None:
+ past_key_values = EncoderDecoderCache(DynamicCache(config=self.config), DynamicCache(config=self.config))
+
+ if encoder_outputs is None:
+ encoder_outputs = self.encoder(
+ input_ids=input_ids,
+ attention_mask=attention_mask,
+ **kwargs,
+ )
+ # If the user passed a tuple for encoder_outputs, we wrap it in a BaseModelOutput
+ elif not isinstance(encoder_outputs, BaseModelOutput):
+ encoder_outputs = BaseModelOutput(
+ last_hidden_state=encoder_outputs[0],
+ hidden_states=encoder_outputs[1] if len(encoder_outputs) > 1 else None,
+ attentions=encoder_outputs[2] if len(encoder_outputs) > 2 else None,
+ )
+
+ # On default we initialize the decoder with bos tokens if nothing has been provided
+ bsz, seq_len, channels = (encoder_outputs[0].shape[0], -1, self.config.decoder_config.num_channels)
+ if decoder_input_ids is None:
+ decoder_input_ids = torch.full(
+ size=(bsz, 1, channels), fill_value=self.config.decoder_config.bos_token_id, device=self.device
+ )
+ # Ensure 3D
+ if decoder_input_ids.ndim == 2:
+ decoder_input_ids = decoder_input_ids.reshape(bsz, channels, seq_len).transpose(1, 2)
+
+ decoder_outputs = self.decoder(
+ input_ids=decoder_input_ids,
+ position_ids=decoder_position_ids,
+ attention_mask=decoder_attention_mask,
+ encoder_hidden_states=encoder_outputs[0],
+ encoder_attention_mask=attention_mask,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ return Seq2SeqModelOutput(
+ last_hidden_state=decoder_outputs.last_hidden_state,
+ past_key_values=decoder_outputs.past_key_values,
+ decoder_hidden_states=decoder_outputs.hidden_states,
+ decoder_attentions=decoder_outputs.attentions,
+ cross_attentions=decoder_outputs.cross_attentions,
+ encoder_last_hidden_state=encoder_outputs[0],
+ encoder_hidden_states=encoder_outputs.hidden_states,
+ encoder_attentions=encoder_outputs.attentions,
+ )
+
+
+@auto_docstring(
+ custom_intro="""
+ The Dia model consisting of a (byte) text encoder and audio decoder with a prediction head on top.
+ """
+)
+class DiaForConditionalGeneration(DiaPreTrainedModel, DiaGenerationMixin):
+ base_model_prefix = "model"
+ output_modalities = ("audio",)
+
+ def __init__(self, config: DiaConfig):
+ super().__init__(config)
+ self.config = config
+ self.model = DiaModel(config)
+
+ self.num_channels = config.decoder_config.num_channels
+ self.vocab_size = config.decoder_config.vocab_size
+ self.logits_dense = nn.Linear(
+ config.decoder_config.hidden_size, (self.num_channels * self.vocab_size), bias=False
+ )
+ self.loss_type = "ForMaskedLM"
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @auto_docstring
+ @can_return_tuple
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.LongTensor | None = None,
+ decoder_input_ids: torch.LongTensor | None = None,
+ decoder_position_ids: torch.LongTensor | None = None,
+ decoder_attention_mask: torch.LongTensor | None = None,
+ encoder_outputs: BaseModelOutput | tuple | None = None,
+ past_key_values: EncoderDecoderCache | None = None,
+ use_cache: bool | None = None,
+ labels: torch.LongTensor | None = None,
+ **kwargs,
+ ) -> tuple | Seq2SeqLMOutput:
+ r"""
+ decoder_input_ids (`torch.LongTensor` of shape `(batch_size * num_codebooks, target_sequence_length)
+ or (batch_size, target_sequence_length, num_codebooks)`, *optional*):
+ 1. (batch_size * num_codebooks, target_sequence_length): corresponds to the general use case where
+ the audio input codebooks are flattened into the batch dimension. This also aligns with the flat-
+ tened audio logits which are used to calculate the loss.
+
+ 2. (batch_size, sequence_length, num_codebooks): corresponds to the internally used shape of
+ Dia to calculate embeddings and subsequent steps more efficiently.
+
+ If no `decoder_input_ids` are provided, it will create a tensor of `bos_token_id` with shape
+ `(batch_size, 1, num_codebooks)`. Indices can be obtained using the [`DiaProcessor`]. See
+ [`DiaProcessor.__call__`] for more details.
+
+ [What are decoder input IDs?](../glossary#decoder-input-ids)
+ decoder_position_ids (`torch.LongTensor` of shape `(batch_size, target_sequence_length)`):
+ Indices of positions of each input sequence tokens in the position embeddings.
+ Used to calculate the position embeddings up to `config.decoder_config.max_position_embeddings`.
+
+ [What are position IDs?](../glossary#position-ids)
+ labels (`torch.LongTensor` of shape `(batch_size * num_codebooks,)`, *optional*):
+ Labels for computing the masked language modeling loss. Indices should either be in
+ `[0, ..., config.decoder_config.vocab_size - 1]` or -100. Tokens with indices set to `-100`
+ are ignored (masked).
+ """
+
+ outputs = self.model(
+ input_ids=input_ids,
+ attention_mask=attention_mask,
+ decoder_input_ids=decoder_input_ids,
+ decoder_position_ids=decoder_position_ids,
+ decoder_attention_mask=decoder_attention_mask,
+ encoder_outputs=encoder_outputs,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ last_hidden_state = outputs[0]
+ batch_size = last_hidden_state.shape[0]
+ # 3D <-> 2D makes it necessary to prioritize channel dim
+ audio_logits = (
+ self.logits_dense(last_hidden_state)
+ .view((batch_size, -1, self.num_channels, self.vocab_size))
+ .transpose(1, 2)
+ .contiguous()
+ .view(batch_size * self.num_channels, -1, self.vocab_size)
+ )
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(logits=audio_logits, labels=labels, vocab_size=self.vocab_size, **kwargs)
+
+ return Seq2SeqLMOutput(
+ loss=loss,
+ logits=audio_logits,
+ past_key_values=outputs.past_key_values,
+ decoder_hidden_states=outputs.decoder_hidden_states,
+ decoder_attentions=outputs.decoder_attentions,
+ cross_attentions=outputs.cross_attentions,
+ encoder_last_hidden_state=outputs.encoder_last_hidden_state,
+ encoder_hidden_states=outputs.encoder_hidden_states,
+ encoder_attentions=outputs.encoder_attentions,
+ )
+
+
+__all__ = ["DiaModel", "DiaPreTrainedModel", "DiaForConditionalGeneration"]
diff --git a/third_party/transformers/src/transformers/models/dia/modular_dia.py b/third_party/transformers/src/transformers/models/dia/modular_dia.py
new file mode 100644
index 0000000000000000000000000000000000000000..80f108e4ca21d70eaa74b9b9ff410511c3dc0e3a
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/dia/modular_dia.py
@@ -0,0 +1,660 @@
+# Copyright 2025 The Nari Labs and HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""PyTorch Dia model."""
+
+from collections.abc import Callable
+
+import torch
+from torch import nn
+
+from ... import initialization as init
+from ...cache_utils import DynamicCache, EncoderDecoderCache
+from ...masking_utils import create_bidirectional_mask, create_causal_mask
+from ...modeling_flash_attention_utils import FlashAttentionKwargs
+from ...modeling_layers import GradientCheckpointingLayer
+from ...modeling_outputs import (
+ BaseModelOutput,
+ BaseModelOutputWithPastAndCrossAttentions,
+ Seq2SeqLMOutput,
+ Seq2SeqModelOutput,
+)
+from ...modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
+from ...processing_utils import Unpack
+from ...utils import TransformersKwargs, auto_docstring, can_return_tuple, is_torchdynamo_compiling, logging
+from ...utils.generic import merge_with_config_defaults
+from ...utils.output_capturing import capture_outputs
+from ..llama.modeling_llama import (
+ LlamaAttention,
+ LlamaRMSNorm,
+ LlamaRotaryEmbedding,
+ eager_attention_forward,
+)
+from ..phi3.modeling_phi3 import Phi3MLP
+from .configuration_dia import DiaConfig, DiaDecoderConfig, DiaEncoderConfig
+from .generation_dia import DiaGenerationMixin
+
+
+logger = logging.get_logger(__name__)
+
+
+@auto_docstring
+class DiaPreTrainedModel(PreTrainedModel):
+ config: DiaConfig
+ base_model_prefix = "model"
+ supports_gradient_checkpointing = True
+ _supports_flash_attn = True
+ _supports_sdpa = True
+ _supports_flex_attn = True
+ _can_compile_fullgraph = True
+ main_input_name = "input_ids"
+ _no_split_modules = ["DiaEncoderLayer", "DiaDecoderLayer"]
+
+ def _init_weights(self, module):
+ super()._init_weights(module)
+ if isinstance(module, DiaMultiChannelEmbedding):
+ offsets = torch.arange(self.config.num_channels, dtype=torch.long) * self.config.vocab_size
+ init.copy_(module.offsets, offsets)
+
+
+class DiaMultiChannelEmbedding(nn.Module):
+ """In order to efficiently compute the audio embedding from the 9 different channels,
+ we vectorize the embedding process by using a single embedding layer and an offset.
+ Example:
+ - num_embeds = 4
+ - vocab_size = 8
+ - num_channels = 3
+ We would have offsets = [0, 8, 16]
+ If audio_codes = [0, 1, 2, 3], [1, 3, 4, 7], [5, 6, 7, 8],
+ then tokens = audio_codes + offsets
+ = [0, 1, 2, 3, 9, 11, 12, 15, 21, 22, 23, 24]
+ This allows us to use a single embedding layer for all channels.
+ """
+
+ def __init__(self, config: DiaDecoderConfig):
+ super().__init__()
+ self.embed = nn.Embedding(config.vocab_size * config.num_channels, config.hidden_size)
+ self.hidden_size = config.hidden_size
+ self.num_channels = config.num_channels
+ offsets = torch.arange(config.num_channels, dtype=torch.long) * config.vocab_size # (C,)
+ self.register_buffer("offsets", offsets, persistent=False)
+
+ def forward(self, audio_codes: torch.Tensor) -> torch.Tensor:
+ tokens = (audio_codes + self.offsets.to(audio_codes.device)).squeeze(1)
+ embeds = self.embed(tokens).view(tokens.shape[0], audio_codes.shape[1], -1, self.hidden_size)
+ return embeds.sum(dim=2)
+
+
+class DiaMLP(Phi3MLP):
+ pass
+
+
+class DiaRMSNorm(LlamaRMSNorm):
+ pass
+
+
+class DiaRotaryEmbedding(LlamaRotaryEmbedding):
+ pass
+
+
+class DiaSelfAttention(LlamaAttention):
+ """Multi-headed attention from 'Attention Is All You Need' paper"""
+
+ def __init__(self, config: DiaEncoderConfig | DiaDecoderConfig, layer_idx: int, is_causal: bool = False):
+ nn.Module.__init__(self)
+ self.config = config
+ self.layer_idx = layer_idx
+ self.hidden_size = config.hidden_size
+ self.num_heads = self.config.num_attention_heads
+ self.num_key_value_heads = self.config.num_key_value_heads or self.num_heads
+ self.num_key_value_groups = self.num_heads // self.num_key_value_heads
+ self.head_dim = getattr(config, "head_dim", config.hidden_size // self.num_heads)
+ self.scaling = 1
+ self.attention_dropout = 0.0
+ self.is_causal = is_causal
+
+ self.q_proj = nn.Linear(self.hidden_size, self.num_heads * self.head_dim, bias=False)
+ self.k_proj = nn.Linear(self.hidden_size, self.num_key_value_heads * self.head_dim, bias=False)
+ self.v_proj = nn.Linear(self.hidden_size, self.num_key_value_heads * self.head_dim, bias=False)
+ self.o_proj = nn.Linear(self.num_heads * self.head_dim, self.hidden_size, bias=False)
+
+
+class DiaCrossAttention(nn.Module):
+ """Multi-headed attention from 'Attention Is All You Need' paper"""
+
+ def __init__(self, config: DiaDecoderConfig, layer_idx: int):
+ super().__init__()
+ self.config = config
+ self.layer_idx = layer_idx
+ self.hidden_size = config.hidden_size
+ self.cross_hidden_size = config.cross_hidden_size
+ self.num_heads = self.config.cross_num_attention_heads
+ self.num_key_value_heads = self.config.cross_num_key_value_heads
+ self.num_key_value_groups = self.num_heads // self.num_key_value_heads
+ self.head_dim = config.cross_head_dim
+ self.scaling = 1
+ self.attention_dropout = 0.0
+ self.is_causal = False
+
+ self.q_proj = nn.Linear(self.hidden_size, self.num_heads * self.head_dim, bias=False)
+ self.k_proj = nn.Linear(self.cross_hidden_size, self.num_key_value_heads * self.head_dim, bias=False)
+ self.v_proj = nn.Linear(self.cross_hidden_size, self.num_key_value_heads * self.head_dim, bias=False)
+ self.o_proj = nn.Linear(self.num_heads * self.head_dim, self.hidden_size, bias=False)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ cross_attention_states: torch.Tensor,
+ attention_mask: torch.Tensor | None = None,
+ past_key_values: EncoderDecoderCache | None = None,
+ **kwargs: Unpack[FlashAttentionKwargs],
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
+ input_shape = hidden_states.shape[:-1]
+ hidden_shape = (*input_shape, -1, self.head_dim)
+ cross_shape = (*cross_attention_states.shape[:-1], -1, self.head_dim)
+
+ query_states = self.q_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+
+ is_updated = past_key_values.is_updated.get(self.layer_idx) if past_key_values is not None else False
+ if past_key_values is not None and is_updated:
+ # reuse k,v, cross_attentions
+ key_states = past_key_values.cross_attention_cache.layers[self.layer_idx].keys
+ value_states = past_key_values.cross_attention_cache.layers[self.layer_idx].values
+ else:
+ key_states = self.k_proj(cross_attention_states).view(cross_shape).transpose(1, 2)
+ value_states = self.v_proj(cross_attention_states).view(cross_shape).transpose(1, 2)
+
+ if past_key_values is not None:
+ # save all states to the cache
+ key_states, value_states = past_key_values.cross_attention_cache.update(
+ key_states,
+ value_states,
+ self.layer_idx,
+ )
+ # set flag that curr layer for cross-attn is already updated so we can re-use in subsequent calls
+ past_key_values.is_updated[self.layer_idx] = True
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ query_states,
+ key_states,
+ value_states,
+ attention_mask,
+ scaling=self.scaling,
+ **kwargs,
+ )
+
+ attn_output = attn_output.reshape((*input_shape, -1)).contiguous()
+ attn_output = self.o_proj(attn_output)
+ return attn_output, attn_weights
+
+
+class DiaEncoderLayer(GradientCheckpointingLayer):
+ def __init__(self, config: DiaEncoderConfig, layer_idx: int):
+ super().__init__()
+ self.pre_sa_norm = DiaRMSNorm(config.hidden_size, eps=config.norm_eps)
+ self.self_attention = DiaSelfAttention(config, layer_idx, is_causal=False)
+ self.post_sa_norm = DiaRMSNorm(config.hidden_size, eps=config.norm_eps)
+ self.mlp = DiaMLP(config)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
+ attention_mask: torch.Tensor | None = None,
+ **kwargs: Unpack[FlashAttentionKwargs],
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
+ residual = hidden_states
+ normed_states = self.pre_sa_norm(hidden_states)
+ self_attn_output, _ = self.self_attention(
+ normed_states,
+ position_embeddings=position_embeddings,
+ attention_mask=attention_mask,
+ **kwargs,
+ )
+ hidden_states = residual + self_attn_output
+
+ residual = hidden_states
+ normed_states = self.post_sa_norm(hidden_states)
+ mlp_out = self.mlp(normed_states)
+ hidden_states = residual + mlp_out
+
+ return hidden_states
+
+
+class DiaEncoder(DiaPreTrainedModel):
+ _can_record_outputs = {
+ "hidden_states": DiaEncoderLayer,
+ "attentions": DiaSelfAttention,
+ }
+
+ def __init__(self, config: DiaEncoderConfig):
+ super().__init__(config)
+ self.config = config
+
+ self.embedding = nn.Embedding(config.vocab_size, config.hidden_size)
+ self.layers = nn.ModuleList(
+ [DiaEncoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
+ )
+ self.norm = DiaRMSNorm(config.hidden_size, eps=config.norm_eps)
+ self.rotary_emb = DiaRotaryEmbedding(config=config)
+
+ self.post_init()
+
+ @merge_with_config_defaults
+ @capture_outputs
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.Tensor,
+ attention_mask: torch.Tensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> BaseModelOutput:
+ hidden_states = self.embedding(input_ids)
+
+ # RoPE
+ # Note: We expect right padding and hence always generate
+ # the position ids on the fly to reduce preparation overhead
+ position_ids = torch.arange(input_ids.shape[-1], device=input_ids.device)[None, :]
+
+ attention_mask = create_bidirectional_mask(
+ config=self.config,
+ inputs_embeds=hidden_states,
+ attention_mask=attention_mask,
+ )
+ position_embeddings = self.rotary_emb(hidden_states, position_ids=position_ids)
+
+ for encoder_layer in self.layers:
+ hidden_states = encoder_layer(
+ hidden_states,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ position_embeddings=position_embeddings,
+ **kwargs,
+ )
+
+ hidden_states = self.norm(hidden_states)
+
+ return BaseModelOutput(last_hidden_state=hidden_states)
+
+
+class DiaDecoderLayer(GradientCheckpointingLayer):
+ def __init__(self, config: DiaDecoderConfig, layer_idx: int):
+ super().__init__()
+ self.embed_dim = config.hidden_size
+ self.self_attention = DiaSelfAttention(config, layer_idx, is_causal=True)
+ self.cross_attention = DiaCrossAttention(config, layer_idx)
+ self.pre_sa_norm = DiaRMSNorm(config.hidden_size, eps=config.norm_eps)
+ self.pre_ca_norm = DiaRMSNorm(config.hidden_size, eps=config.norm_eps)
+ self.pre_mlp_norm = DiaRMSNorm(config.hidden_size, eps=config.norm_eps)
+ self.mlp = DiaMLP(config)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
+ attention_mask: torch.Tensor | None = None,
+ encoder_hidden_states: torch.Tensor | None = None,
+ encoder_attention_mask: torch.Tensor | None = None,
+ past_key_values: EncoderDecoderCache | None = None,
+ **kwargs,
+ ) -> tuple[torch.Tensor, torch.Tensor | None, torch.Tensor | None]:
+ self_attn_cache = past_key_values
+ if isinstance(self_attn_cache, EncoderDecoderCache):
+ self_attn_cache = self_attn_cache.self_attention_cache
+
+ residual = hidden_states
+ normed_states = self.pre_sa_norm(hidden_states)
+ self_attn_output, _ = self.self_attention(
+ normed_states,
+ position_embeddings,
+ attention_mask,
+ # Needs to be an arg in order to function properly
+ # on inplace operations to be carried (e.g. compile)
+ self_attn_cache,
+ **kwargs,
+ )
+ hidden_states = residual + self_attn_output
+
+ residual = hidden_states
+ normed_states = self.pre_ca_norm(hidden_states)
+ cross_states, _ = self.cross_attention(
+ normed_states,
+ encoder_hidden_states,
+ attention_mask=encoder_attention_mask,
+ past_key_values=past_key_values,
+ **kwargs,
+ )
+ hidden_states = residual + cross_states
+
+ residual = hidden_states
+ normed_states = self.pre_mlp_norm(hidden_states)
+ mlp_out = self.mlp(normed_states)
+ hidden_states = residual + mlp_out
+
+ return hidden_states
+
+
+class DiaDecoder(DiaPreTrainedModel):
+ """Transformer Decoder Stack using DenseGeneral."""
+
+ _can_record_outputs = {
+ "hidden_states": DiaDecoderLayer,
+ "attentions": [DiaSelfAttention, DiaCrossAttention],
+ }
+
+ def __init__(self, config: DiaDecoderConfig):
+ super().__init__(config)
+ self.num_channels = config.num_channels
+ self.vocab_size = config.vocab_size
+ self.embeddings = DiaMultiChannelEmbedding(config)
+ self.layers = nn.ModuleList(
+ [DiaDecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
+ )
+ self.norm = DiaRMSNorm(config.hidden_size, eps=config.norm_eps)
+ self.rotary_emb = DiaRotaryEmbedding(config=config)
+
+ self.post_init()
+
+ @merge_with_config_defaults
+ @capture_outputs
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.Tensor,
+ position_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ encoder_hidden_states: torch.FloatTensor | None = None,
+ encoder_attention_mask: torch.LongTensor | None = None,
+ past_key_values: EncoderDecoderCache | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> BaseModelOutputWithPastAndCrossAttentions | tuple:
+ r"""
+ input_ids (`torch.LongTensor` of shape `(batch_size, sequence_length, num_codebooks)`):
+ The original `decoder_input_ids` in 3D shape to facilitate more efficient computations.
+
+ [What are input IDs?](../glossary#input-ids)
+ """
+
+ batch_size, seq_length = input_ids.size()[:-1]
+ past_key_values_length = past_key_values.get_seq_length() if past_key_values is not None else 0
+
+ if position_ids is None:
+ position_ids = torch.arange(seq_length, device=input_ids.device) + past_key_values_length
+ position_ids = position_ids.unsqueeze(0)
+
+ # RoPE
+ hidden_states = self.embeddings(input_ids)
+
+ if attention_mask is None and not is_torchdynamo_compiling():
+ # required mask seq length can be calculated via length of past cache
+ mask_seq_length = past_key_values_length + seq_length
+ attention_mask = torch.ones(batch_size, mask_seq_length, device=input_ids.device)
+
+ attention_mask = create_causal_mask(
+ config=self.config,
+ inputs_embeds=hidden_states,
+ attention_mask=attention_mask,
+ past_key_values=past_key_values,
+ )
+ encoder_attention_mask = create_bidirectional_mask(
+ config=self.config,
+ inputs_embeds=hidden_states,
+ attention_mask=encoder_attention_mask,
+ encoder_hidden_states=encoder_hidden_states,
+ )
+ position_embeddings = self.rotary_emb(hidden_states, position_ids=position_ids)
+
+ for layer in self.layers:
+ hidden_states = layer(
+ hidden_states,
+ # Needs to be an arg in order to function properly
+ # on inplace operations to be carried (e.g. compile)
+ position_embeddings,
+ attention_mask,
+ encoder_hidden_states,
+ encoder_attention_mask=encoder_attention_mask,
+ past_key_values=past_key_values,
+ position_ids=position_ids,
+ **kwargs,
+ )
+
+ hidden_states = self.norm(hidden_states)
+
+ return BaseModelOutputWithPastAndCrossAttentions(
+ last_hidden_state=hidden_states,
+ past_key_values=past_key_values,
+ )
+
+
+@auto_docstring(
+ custom_intro="""
+ The bare Dia model outputting raw hidden-states without any specific head on top.
+ """
+)
+class DiaModel(DiaPreTrainedModel):
+ def __init__(self, config: DiaConfig):
+ super().__init__(config)
+ self.config = config
+ self.encoder = DiaEncoder(config.encoder_config)
+ self.decoder = DiaDecoder(config.decoder_config)
+ self.post_init()
+
+ @auto_docstring
+ @can_return_tuple
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.LongTensor | None = None,
+ decoder_input_ids: torch.LongTensor | None = None,
+ decoder_position_ids: torch.LongTensor | None = None,
+ decoder_attention_mask: torch.LongTensor | None = None,
+ encoder_outputs: BaseModelOutput | tuple | None = None,
+ past_key_values: EncoderDecoderCache | None = None,
+ use_cache: bool | None = None,
+ **kwargs,
+ ) -> tuple | Seq2SeqModelOutput:
+ r"""
+ decoder_input_ids (`torch.LongTensor` of shape `(batch_size * num_codebooks, target_sequence_length)
+ or (batch_size, target_sequence_length, num_codebooks)`, *optional*):
+ 1. (batch_size * num_codebooks, target_sequence_length): corresponds to the general use case where
+ the audio input codebooks are flattened into the batch dimension. This also aligns with the flat-
+ tened audio logits which are used to calculate the loss.
+
+ 2. (batch_size, sequence_length, num_codebooks): corresponds to the internally used shape of
+ Dia to calculate embeddings and subsequent steps more efficiently.
+
+ If no `decoder_input_ids` are provided, it will create a tensor of `bos_token_id` with shape
+ `(batch_size, 1, num_codebooks)`. Indices can be obtained using the [`DiaProcessor`]. See
+ [`DiaProcessor.__call__`] for more details.
+
+ [What are decoder input IDs?](../glossary#decoder-input-ids)
+ decoder_position_ids (`torch.LongTensor` of shape `(batch_size, target_sequence_length)`):
+ Indices of positions of each input sequence tokens in the position embeddings.
+ Used to calculate the position embeddings up to `config.decoder_config.max_position_embeddings`.
+
+ [What are position IDs?](../glossary#position-ids)
+ """
+
+ if input_ids is None and encoder_outputs is None:
+ raise ValueError(
+ "You should either provide text ids or the cached text encodings. Neither has been found."
+ )
+
+ if self.is_gradient_checkpointing and self.training:
+ if use_cache:
+ logger.warning_once(
+ "`use_cache=True` is incompatible with gradient checkpointing. Setting `use_cache=False`..."
+ )
+ use_cache = False
+
+ if use_cache and past_key_values is None:
+ past_key_values = EncoderDecoderCache(DynamicCache(config=self.config), DynamicCache(config=self.config))
+
+ if encoder_outputs is None:
+ encoder_outputs = self.encoder(
+ input_ids=input_ids,
+ attention_mask=attention_mask,
+ **kwargs,
+ )
+ # If the user passed a tuple for encoder_outputs, we wrap it in a BaseModelOutput
+ elif not isinstance(encoder_outputs, BaseModelOutput):
+ encoder_outputs = BaseModelOutput(
+ last_hidden_state=encoder_outputs[0],
+ hidden_states=encoder_outputs[1] if len(encoder_outputs) > 1 else None,
+ attentions=encoder_outputs[2] if len(encoder_outputs) > 2 else None,
+ )
+
+ # On default we initialize the decoder with bos tokens if nothing has been provided
+ bsz, seq_len, channels = (encoder_outputs[0].shape[0], -1, self.config.decoder_config.num_channels)
+ if decoder_input_ids is None:
+ decoder_input_ids = torch.full(
+ size=(bsz, 1, channels), fill_value=self.config.decoder_config.bos_token_id, device=self.device
+ )
+ # Ensure 3D
+ if decoder_input_ids.ndim == 2:
+ decoder_input_ids = decoder_input_ids.reshape(bsz, channels, seq_len).transpose(1, 2)
+
+ decoder_outputs = self.decoder(
+ input_ids=decoder_input_ids,
+ position_ids=decoder_position_ids,
+ attention_mask=decoder_attention_mask,
+ encoder_hidden_states=encoder_outputs[0],
+ encoder_attention_mask=attention_mask,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ return Seq2SeqModelOutput(
+ last_hidden_state=decoder_outputs.last_hidden_state,
+ past_key_values=decoder_outputs.past_key_values,
+ decoder_hidden_states=decoder_outputs.hidden_states,
+ decoder_attentions=decoder_outputs.attentions,
+ cross_attentions=decoder_outputs.cross_attentions,
+ encoder_last_hidden_state=encoder_outputs[0],
+ encoder_hidden_states=encoder_outputs.hidden_states,
+ encoder_attentions=encoder_outputs.attentions,
+ )
+
+
+@auto_docstring(
+ custom_intro="""
+ The Dia model consisting of a (byte) text encoder and audio decoder with a prediction head on top.
+ """
+)
+class DiaForConditionalGeneration(DiaPreTrainedModel, DiaGenerationMixin):
+ base_model_prefix = "model"
+ output_modalities = ("audio",)
+
+ def __init__(self, config: DiaConfig):
+ super().__init__(config)
+ self.config = config
+ self.model = DiaModel(config)
+
+ self.num_channels = config.decoder_config.num_channels
+ self.vocab_size = config.decoder_config.vocab_size
+ self.logits_dense = nn.Linear(
+ config.decoder_config.hidden_size, (self.num_channels * self.vocab_size), bias=False
+ )
+ self.loss_type = "ForMaskedLM"
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @auto_docstring
+ @can_return_tuple
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.LongTensor | None = None,
+ decoder_input_ids: torch.LongTensor | None = None,
+ decoder_position_ids: torch.LongTensor | None = None,
+ decoder_attention_mask: torch.LongTensor | None = None,
+ encoder_outputs: BaseModelOutput | tuple | None = None,
+ past_key_values: EncoderDecoderCache | None = None,
+ use_cache: bool | None = None,
+ labels: torch.LongTensor | None = None,
+ **kwargs,
+ ) -> tuple | Seq2SeqLMOutput:
+ r"""
+ decoder_input_ids (`torch.LongTensor` of shape `(batch_size * num_codebooks, target_sequence_length)
+ or (batch_size, target_sequence_length, num_codebooks)`, *optional*):
+ 1. (batch_size * num_codebooks, target_sequence_length): corresponds to the general use case where
+ the audio input codebooks are flattened into the batch dimension. This also aligns with the flat-
+ tened audio logits which are used to calculate the loss.
+
+ 2. (batch_size, sequence_length, num_codebooks): corresponds to the internally used shape of
+ Dia to calculate embeddings and subsequent steps more efficiently.
+
+ If no `decoder_input_ids` are provided, it will create a tensor of `bos_token_id` with shape
+ `(batch_size, 1, num_codebooks)`. Indices can be obtained using the [`DiaProcessor`]. See
+ [`DiaProcessor.__call__`] for more details.
+
+ [What are decoder input IDs?](../glossary#decoder-input-ids)
+ decoder_position_ids (`torch.LongTensor` of shape `(batch_size, target_sequence_length)`):
+ Indices of positions of each input sequence tokens in the position embeddings.
+ Used to calculate the position embeddings up to `config.decoder_config.max_position_embeddings`.
+
+ [What are position IDs?](../glossary#position-ids)
+ labels (`torch.LongTensor` of shape `(batch_size * num_codebooks,)`, *optional*):
+ Labels for computing the masked language modeling loss. Indices should either be in
+ `[0, ..., config.decoder_config.vocab_size - 1]` or -100. Tokens with indices set to `-100`
+ are ignored (masked).
+ """
+
+ outputs = self.model(
+ input_ids=input_ids,
+ attention_mask=attention_mask,
+ decoder_input_ids=decoder_input_ids,
+ decoder_position_ids=decoder_position_ids,
+ decoder_attention_mask=decoder_attention_mask,
+ encoder_outputs=encoder_outputs,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ last_hidden_state = outputs[0]
+ batch_size = last_hidden_state.shape[0]
+ # 3D <-> 2D makes it necessary to prioritize channel dim
+ audio_logits = (
+ self.logits_dense(last_hidden_state)
+ .view((batch_size, -1, self.num_channels, self.vocab_size))
+ .transpose(1, 2)
+ .contiguous()
+ .view(batch_size * self.num_channels, -1, self.vocab_size)
+ )
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(logits=audio_logits, labels=labels, vocab_size=self.vocab_size, **kwargs)
+
+ return Seq2SeqLMOutput(
+ loss=loss,
+ logits=audio_logits,
+ past_key_values=outputs.past_key_values,
+ decoder_hidden_states=outputs.decoder_hidden_states,
+ decoder_attentions=outputs.decoder_attentions,
+ cross_attentions=outputs.cross_attentions,
+ encoder_last_hidden_state=outputs.encoder_last_hidden_state,
+ encoder_hidden_states=outputs.encoder_hidden_states,
+ encoder_attentions=outputs.encoder_attentions,
+ )
+
+
+__all__ = ["DiaModel", "DiaPreTrainedModel", "DiaForConditionalGeneration"]
diff --git a/third_party/transformers/src/transformers/models/dia/processing_dia.py b/third_party/transformers/src/transformers/models/dia/processing_dia.py
new file mode 100644
index 0000000000000000000000000000000000000000..a2256f348dfa5c3e1c450d7be48329bba6661b60
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/dia/processing_dia.py
@@ -0,0 +1,482 @@
+# Copyright 2025 The HuggingFace Inc. team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Processor class for Dia"""
+
+import math
+from pathlib import Path
+
+from ...audio_utils import AudioInput, make_list_of_audio
+from ...feature_extraction_utils import BatchFeature
+from ...processing_utils import AudioKwargs, ProcessingKwargs, ProcessorMixin, Unpack
+from ...utils import auto_docstring, is_soundfile_available, is_torch_available
+
+
+if is_torch_available():
+ import torch
+
+if is_soundfile_available():
+ import soundfile as sf
+
+
+class DiaAudioKwargs(AudioKwargs, total=False):
+ """
+ bos_token_id (`int`, *optional*, defaults to `1026`):
+ The token ID used as the beginning-of-sequence token for audio codebooks. This token is prepended to each
+ audio sequence during encoding.
+ eos_token_id (`int`, *optional*, defaults to `1024`):
+ The token ID used as the end-of-sequence token for audio codebooks. This token is appended to audio sequences
+ during training (when `generation=False`) to mark the end of the audio.
+ pad_token_id (`int`, *optional*, defaults to `1025`):
+ The token ID used for padding audio codebook sequences. This token is used to fill positions in the delay
+ pattern where no valid audio token exists.
+ delay_pattern (`list[int]`, *optional*, defaults to `[0, 8, 9, 10, 11, 12, 13, 14, 15]`):
+ A list of delay values (in frames) for each codebook channel. The delay pattern creates temporal offsets
+ between different codebook channels, allowing the model to capture dependencies across channels. Each value
+ represents the number of frames to delay that specific channel.
+ generation (`bool`, *optional*, defaults to `True`):
+ Whether the processor is being used for generation (text-to-speech) or training. When `True`, the processor
+ prepares inputs for generation mode where audio is generated from text. When `False`, it prepares inputs for
+ training where both text and audio are provided.
+ """
+
+ bos_token_id: int
+ eos_token_id: int
+ pad_token_id: int
+ delay_pattern: list[int]
+ generation: bool
+
+
+class DiaProcessorKwargs(ProcessingKwargs, total=False):
+ audio_kwargs: DiaAudioKwargs
+ _defaults = {
+ "text_kwargs": {
+ "padding": True,
+ "padding_side": "right",
+ "add_special_tokens": False,
+ },
+ "audio_kwargs": {
+ "eos_token_id": 1024,
+ "pad_token_id": 1025,
+ "bos_token_id": 1026,
+ "delay_pattern": [0, 8, 9, 10, 11, 12, 13, 14, 15],
+ "generation": True,
+ "sampling_rate": 44100,
+ },
+ "common_kwargs": {
+ "return_tensors": "pt",
+ },
+ }
+
+
+@auto_docstring
+class DiaProcessor(ProcessorMixin):
+ audio_tokenizer_class = "DacModel"
+
+ def __init__(self, feature_extractor, tokenizer, audio_tokenizer):
+ r"""
+ audio_tokenizer (`DacModel`):
+ An instance of [`DacModel`] used to encode/decode audio into/from codebooks. It is a required input.
+ """
+ super().__init__(feature_extractor, tokenizer, audio_tokenizer=audio_tokenizer)
+
+ @auto_docstring
+ def __call__(
+ self,
+ text: str | list[str],
+ audio: AudioInput | None = None,
+ output_labels: bool | None = False,
+ **kwargs: Unpack[DiaProcessorKwargs],
+ ):
+ r"""
+ output_labels (`bool`, *optional*, defaults to `False`):
+ Whether to return labels for training. When `True`, the processor generates labels from the decoder input
+ sequence by shifting it by one position. Labels use special values: `-100` for tokens to ignore in loss
+ computation (padding and BOS tokens), and `-101` for audio frames used only for the backbone model (when
+ `depth_decoder_labels_ratio < 1.0`). Cannot be used together with `generation=True`.
+ """
+ if not is_torch_available():
+ raise ValueError(
+ "The `DiaProcessor` relies on the `audio_tokenizer` which requires `torch` but we couldn't "
+ "find it in your environment. You can install torch via `pip install torch`."
+ )
+
+ if text is None:
+ raise ValueError("You need to specify the `text` input to process.")
+
+ output_kwargs = self._merge_kwargs(
+ DiaProcessorKwargs,
+ **kwargs,
+ )
+
+ text_kwargs = output_kwargs["text_kwargs"]
+ audio_kwargs = output_kwargs["audio_kwargs"]
+ return_tensors = text_kwargs.get("return_tensors", None)
+ if return_tensors != "pt":
+ raise ValueError(f"{self.__class__.__name__} only supports `return_tensors='pt'`.")
+
+ data = {}
+
+ # Text
+ if isinstance(text, str):
+ text = [text]
+ elif not (isinstance(text, (list, tuple)) and all(isinstance(t, str) for t in text)):
+ raise ValueError("Invalid input text. Please provide a string, or a list of strings")
+
+ encodings = self.tokenizer(text, **text_kwargs)
+ data.update(encodings)
+
+ # Audio
+ delay_pattern = audio_kwargs.pop("delay_pattern", None)
+ audio_bos_token_id = audio_kwargs.pop("bos_token_id", None)
+ audio_eos_token_id = audio_kwargs.pop("eos_token_id", None)
+ audio_pad_token_id = audio_kwargs.pop("pad_token_id", None)
+ generation = audio_kwargs.pop("generation", True)
+ if (
+ audio_bos_token_id is None
+ or audio_eos_token_id is None
+ or audio_pad_token_id is None
+ or delay_pattern is None
+ ):
+ raise ValueError(
+ "To enable processing for Dia, we need the `bos_token_id`, `eos_token_id`, "
+ "`pad_token_id`, and `delay_pattern`. You may have accidentally overwritten one of those."
+ )
+
+ if generation and output_labels:
+ raise ValueError(
+ f"Labels with `generation` is incompatible, got generation={generation}, output_labels={output_labels}."
+ )
+
+ batch_size = data["input_ids"].shape[0]
+ num_channels = len(delay_pattern)
+ max_delay = max(delay_pattern)
+
+ # Voice cloning generation / general training
+ if audio is not None:
+ audio = make_list_of_audio(audio)
+ input_audios = self.feature_extractor(audio, **audio_kwargs)
+
+ compression_rate = math.prod(self.audio_tokenizer.config.downsampling_ratios)
+ max_encoded_sequence_len = input_audios["padding_mask"][0].shape[-1] // compression_rate
+
+ decoder_input_ids = []
+ decoder_attention_mask = []
+ # TODO: dac with batching is currently broken, but non-batch is working
+ # refer to https://gist.github.com/vasqu/643a45b680cf39fd7467271ee2eb6f80 for a validation script
+ for padding_mask, audio in zip(input_audios["padding_mask"], input_audios["input_values"]):
+ # get current length with hop length in mind (as if it were sampled as a single audio)
+ base_pad_len = self.feature_extractor.hop_length
+ current_audio_len = math.ceil(padding_mask.sum(dim=-1) / base_pad_len) * base_pad_len
+
+ encoded_sequence_len = current_audio_len // compression_rate
+ padding_len = max_encoded_sequence_len - encoded_sequence_len
+
+ # compute non-padded forward pass; one extra bos (and eos if training) is added
+ with torch.no_grad():
+ audio = audio[None, ..., :current_audio_len].to(self.audio_tokenizer.device)
+ input_ids = self.audio_tokenizer.encode(audio).audio_codes.transpose(1, 2)
+
+ if not generation:
+ input_ids = torch.nn.functional.pad(
+ input_ids, pad=(0, 0, 0, 1, 0, 0), mode="constant", value=audio_eos_token_id
+ )
+
+ # apply padding
+ # +1 for the bos within the real sequence
+ input_ids = torch.nn.functional.pad(
+ input_ids, pad=(0, 0, padding_len + 1, 0, 0, 0), mode="constant", value=audio_bos_token_id
+ )
+ num_valid_inputs = encoded_sequence_len + 1 + max_delay # sequence + bos + delay
+ num_valid_inputs += 0 if generation else 1 # eos if training
+ attention_mask = torch.tensor([0] * padding_len + [1] * num_valid_inputs, dtype=torch.long)[None, :]
+
+ decoder_input_ids.append(input_ids)
+ decoder_attention_mask.append(attention_mask)
+
+ decoder_input_ids = torch.cat(decoder_input_ids, dim=0)
+ decoder_attention_mask = torch.cat(decoder_attention_mask, dim=0)
+ # TTS generation
+ elif generation:
+ # all bos to start with TTS
+ decoder_input_ids = torch.full((batch_size, 1, num_channels), audio_bos_token_id, dtype=torch.long)
+
+ # we preemptively add the delay
+ decoder_attention_mask = torch.ones(size=(batch_size, 1 + max_delay), dtype=torch.long)
+ else:
+ raise ValueError("If you try to train, you should provide audio data as well.")
+
+ if batch_size != decoder_input_ids.shape[0]:
+ raise ValueError(
+ f"Need the same amount of samples for both text and audio, but got text samples={batch_size} and "
+ f"audio samples = {decoder_input_ids.shape[0]} instead."
+ )
+
+ # prepare shift indices per delay
+ max_seq_len = decoder_attention_mask.shape[-1]
+ max_audio_len = max_seq_len - max_delay
+ precomputed_idx = self.build_indices(
+ bsz=batch_size,
+ seq_len=max_seq_len,
+ num_channels=num_channels,
+ delay_pattern=delay_pattern,
+ revert=False,
+ )
+
+ # create delay pattern input
+ # the pad token will be used for masking which input is valid for prediction during generation
+ prefill = torch.full(
+ (batch_size, max_seq_len, num_channels),
+ fill_value=audio_pad_token_id,
+ dtype=torch.int,
+ )
+ prefill[:, :max_audio_len] = decoder_input_ids
+
+ delayed_decoder_input_ids = self.apply_audio_delay(
+ audio=prefill,
+ pad_token_id=audio_pad_token_id,
+ bos_token_id=audio_bos_token_id,
+ precomputed_idx=precomputed_idx,
+ )
+
+ data.update({"decoder_input_ids": delayed_decoder_input_ids, "decoder_attention_mask": decoder_attention_mask})
+
+ if output_labels:
+ # Base idea is to shift on the sequence dim
+ labels = data["decoder_input_ids"].clone()[:, 1:]
+ labels[labels == audio_pad_token_id] = -100
+ labels[labels == audio_bos_token_id] = -100
+
+ data["labels"] = labels.transpose(1, 2).reshape(batch_size * num_channels, -1).contiguous().long()
+ data["decoder_input_ids"] = data["decoder_input_ids"][:, :-1]
+ data["decoder_attention_mask"] = data["decoder_attention_mask"][:, :-1]
+
+ return BatchFeature(data=data, tensor_type=return_tensors)
+
+ def batch_decode(
+ self,
+ decoder_input_ids: "torch.Tensor",
+ audio_prompt_len: int | None = None,
+ **kwargs: Unpack[DiaProcessorKwargs],
+ ) -> list["torch.Tensor"]:
+ """
+ Decodes a batch of audio codebook sequences into their respective audio waveforms via the
+ `audio_tokenizer`. See [`~DacModel.decode`] for more information.
+
+ Args:
+ decoder_input_ids (`torch.Tensor`): The complete output sequence of the decoder.
+ audio_prompt_len (`int`): The audio prefix length (e.g. when using voice cloning).
+ """
+ output_kwargs = self._merge_kwargs(
+ DiaProcessorKwargs,
+ **kwargs,
+ )
+ audio_kwargs = output_kwargs["audio_kwargs"]
+
+ delay_pattern = audio_kwargs.pop("delay_pattern", None)
+ audio_bos_token_id = audio_kwargs.pop("bos_token_id", None)
+ audio_pad_token_id = audio_kwargs.pop("pad_token_id", None)
+ if audio_bos_token_id is None or audio_pad_token_id is None or delay_pattern is None:
+ raise ValueError(
+ "To enable decoding for Dia, we need the `bos_token_id`, `pad_token_id`, "
+ "and `delay_pattern`. You may have accidentally overwritten one of those."
+ )
+
+ # either decode the whole audio sequence or only the generated parts
+ if audio_prompt_len is not None:
+ audio_prompt_len = torch.tensor(audio_prompt_len, device=decoder_input_ids.device, dtype=torch.long)
+ start_of_generation_idx = audio_prompt_len[None].expand(decoder_input_ids.shape[0])
+ else:
+ start_of_generation_idx = (decoder_input_ids[:, :, 0] == audio_bos_token_id).sum(dim=-1)
+ # -1 for the eos token
+ end_of_generation_idx = (
+ decoder_input_ids.shape[1] - (decoder_input_ids[:, :, 0] == audio_pad_token_id).sum(dim=-1) - 1
+ )
+
+ # revert delay
+ bsz, seq_len, num_channels = decoder_input_ids.shape
+ precomputed_idx = self.build_indices(
+ bsz=bsz,
+ seq_len=seq_len,
+ num_channels=num_channels,
+ delay_pattern=delay_pattern,
+ revert=True,
+ )
+
+ output_sequences = self.apply_audio_delay(
+ audio=decoder_input_ids,
+ # We do not care about these values as we cut them out
+ # with `start_of_generation_idx` and `end_of_generation_idx`
+ pad_token_id=-1,
+ bos_token_id=-1,
+ precomputed_idx=precomputed_idx,
+ ).transpose(1, 2)
+
+ # retrieve the correct sequences each
+ audios = []
+ # TODO: see above, dac doesn't work in batches yet
+ with torch.no_grad():
+ for i in range(start_of_generation_idx.shape[0]):
+ output_i = output_sequences[i, :, start_of_generation_idx[i] : end_of_generation_idx[i]][None, ...]
+ output_i = output_i.to(self.audio_tokenizer.device)
+ audio_i = self.audio_tokenizer.decode(audio_codes=output_i).audio_values.cpu().squeeze()
+ audios.append(audio_i)
+
+ return audios
+
+ def decode(
+ self,
+ decoder_input_ids: "torch.Tensor",
+ audio_prompt_len: int | None = None,
+ **kwargs: Unpack[DiaProcessorKwargs],
+ ) -> "torch.Tensor":
+ """
+ Decodes a single sequence of audio codebooks into the respective audio waveform via the
+ `audio_tokenizer`. See [`~DacModel.decode`] and [`~DiaProcessor.batch_decode`] for more information.
+ """
+ if decoder_input_ids.shape[0] != 1:
+ raise ValueError(
+ f"Expecting a single output to be decoded but received {decoder_input_ids.shape[0]} samples instead."
+ )
+
+ return self.batch_decode(decoder_input_ids, audio_prompt_len, **kwargs)[0]
+
+ def get_audio_prompt_len(
+ self,
+ decoder_attention_mask: "torch.Tensor",
+ **kwargs: Unpack[DiaProcessorKwargs],
+ ) -> int:
+ """Utility function to get the audio prompt length."""
+ output_kwargs = self._merge_kwargs(
+ DiaProcessorKwargs,
+ **kwargs,
+ )
+ audio_kwargs = output_kwargs["audio_kwargs"]
+
+ delay_pattern = audio_kwargs.pop("delay_pattern", None)
+ if delay_pattern is None:
+ raise ValueError(
+ "To enable the utility of retrieving the prompt length for Dia, we need the "
+ "`delay_pattern`. You may have accidentally overwritten this."
+ )
+ return decoder_attention_mask.shape[1] - max(delay_pattern)
+
+ # Copied from transformers.models.csm.processing_csm.CsmProcessor.save_audio with Csm->Dia
+ def save_audio(
+ self,
+ audio: AudioInput,
+ saving_path: str | Path | list[str | Path],
+ **kwargs: Unpack[DiaProcessorKwargs],
+ ):
+ # TODO: @eustlb, this should be in AudioProcessor
+ if not is_soundfile_available():
+ raise ImportError("Please install `soundfile` to save audio files.")
+
+ # ensure correct audio input
+ audio = make_list_of_audio(audio)
+
+ # ensure correct saving path
+ if isinstance(saving_path, (str, Path)):
+ saving_path = [saving_path]
+ elif not (isinstance(saving_path, (list, tuple)) and all(isinstance(p, (str, Path)) for p in saving_path)):
+ raise ValueError("Invalid input path. Please provide a string, or a list of strings")
+
+ if len(audio) != len(saving_path):
+ raise ValueError("The number of audio and saving paths must be the same")
+
+ output_kwargs = self._merge_kwargs(
+ DiaProcessorKwargs,
+ **kwargs,
+ )
+ audio_kwargs = output_kwargs["audio_kwargs"]
+ sampling_rate = audio_kwargs["sampling_rate"]
+
+ for audio_value, p in zip(audio, saving_path):
+ if isinstance(audio_value, torch.Tensor):
+ audio_value = audio_value.cpu().float().numpy()
+ sf.write(p, audio_value, sampling_rate)
+
+ @staticmethod
+ def build_indices(
+ bsz: int,
+ seq_len: int,
+ num_channels: int,
+ delay_pattern: list[int],
+ revert: bool = False,
+ ) -> tuple["torch.Tensor", "torch.Tensor"]:
+ """
+ Precompute (sequence_idx, all_idx) so that out[seq, channel] = in[seq - delay[channel], channel]
+ or in[seq, channel] = out[seq + delay[channel], channel] if `revert`.
+ Negative sequence_idx => BOS; sequence_idx >= seq_len => PAD.
+ """
+ delay_array = torch.tensor(delay_pattern, dtype=torch.int32)
+
+ # (0..seq_len-1)
+ sequence_idx = torch.arange(seq_len, dtype=torch.int32)[None, :].expand(bsz, seq_len)[..., None]
+ # + or - delay depending if we delay or revert the delay
+ if not revert:
+ sequence_idx = sequence_idx - delay_array[None, None, :]
+ else:
+ sequence_idx = sequence_idx + delay_array[None, None, :]
+ # if delay goes over the range we clamp back to valid values
+ valid_sequence_idx = torch.clamp(sequence_idx, 0, seq_len - 1)
+
+ batch_idx = torch.arange(bsz, dtype=torch.int32)[:, None, None].expand(bsz, seq_len, num_channels)
+ channel_idx = torch.arange(num_channels, dtype=torch.int32)[None, None, :].expand(bsz, seq_len, num_channels)
+
+ all_idx = torch.stack(
+ [batch_idx.reshape(-1), valid_sequence_idx.reshape(-1), channel_idx.reshape(-1)],
+ dim=1,
+ ).long()
+
+ return sequence_idx, all_idx
+
+ @staticmethod
+ def apply_audio_delay(
+ audio: "torch.Tensor",
+ pad_token_id: int,
+ bos_token_id: int,
+ precomputed_idx: tuple["torch.Tensor", "torch.Tensor"],
+ ) -> "torch.Tensor":
+ """
+ Applies or reverts the delay pattern to batched audio tokens using precomputed indices,
+ inserting BOS where sequence_idx < 0 and PAD where sequence_idx >= seq_len.
+
+ Args:
+ audio: audio tokens of shape [bsz, seq_len, num_channels]
+ pad_token_id: the PAD token
+ bos_token_id: the BOS token
+ precomputed_idx: from `build_indices`
+
+ Returns:
+ final_audio: delayed or reverted audio tokens of shape [bsz, seq_len, num_channels]
+ """
+ # Move everything to the same device
+ device = audio.device
+ sequence_idx, all_idx = precomputed_idx
+ sequence_idx = sequence_idx.to(device)
+ all_idx = all_idx.to(device)
+
+ # Gather per precomputed indices
+ batch_idx, valid_sequence_idx, channel_idx = torch.unbind(all_idx, dim=-1)
+ gathered_audio = audio[batch_idx, valid_sequence_idx, channel_idx].view(audio.size())
+
+ # Mask according to negative sequence_idx => BOS; sequence_idx >= seq_len => PAD
+ mask_bos = sequence_idx < 0
+ mask_pad = sequence_idx >= audio.shape[1]
+ final_audio = torch.where(mask_bos, bos_token_id, torch.where(mask_pad, pad_token_id, gathered_audio))
+
+ return final_audio
+
+
+__all__ = ["DiaProcessor"]
diff --git a/third_party/transformers/src/transformers/models/dia/tokenization_dia.py b/third_party/transformers/src/transformers/models/dia/tokenization_dia.py
new file mode 100644
index 0000000000000000000000000000000000000000..fc99b500d5296757e4d234aa3391a6cb1d1f70ed
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/dia/tokenization_dia.py
@@ -0,0 +1,115 @@
+# Copyright 2025 The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Tokenization class for Dia."""
+
+from ...tokenization_python import AddedToken, PreTrainedTokenizer
+from ...utils import logging
+
+
+logger = logging.get_logger(__name__)
+
+
+class DiaTokenizer(PreTrainedTokenizer):
+ """
+ Construct a Dia tokenizer. Dia simply uses raw bytes utf-8 encoding except for special tokens `[S1]` and `[S2]`.
+
+ This tokenizer inherits from [`PreTrainedTokenizerFast`] which contains most of the main methods. Users should
+ refer to this superclass for more information regarding those methods.
+
+ Args:
+ pad_token (`str`, *optional*, defaults to `""`):
+ The token used for padding, for example when batching sequences of different lengths.
+ unk_token (`str`, *optional*, defaults to `""`):
+ The unknown token. A token that is not in the vocabulary cannot be converted to an ID and is set to be this
+ token instead.
+ max_length (`int`, *optional*, defaults to 1024):
+ The maximum length of the sequences when encoding. Sequences longer than this will be truncated.
+ offset (`int`, *optional*, defaults to 0):
+ The offset of the tokenizer.
+ """
+
+ model_input_names = ["input_ids", "attention_mask"]
+
+ def __init__(
+ self,
+ pad_token: str | None = "",
+ unk_token: str | None = "",
+ max_length: int | None = 1024,
+ offset: int = 0,
+ **kwargs,
+ ):
+ # We have no eos/bos tokens but allow padding -- no l/r strip as we treat them as tokens as well
+ pad_token = AddedToken(pad_token) if isinstance(pad_token, str) else pad_token
+ unk_token = AddedToken(unk_token) if isinstance(unk_token, str) else unk_token
+
+ self._utf_vocab_size = 2**8 # utf is 8 bits
+ self._added_tokens_decoder = {0: pad_token, 1: AddedToken("[S1]"), 2: AddedToken("[S2]")}
+ self.offset = offset
+ super().__init__(
+ unk_token=unk_token,
+ pad_token=pad_token,
+ max_length=max_length,
+ offset=offset,
+ token_type_ids_pattern="all_zeros",
+ token_type_ids_include_special_tokens=True,
+ special_tokens_pattern="none",
+ **kwargs,
+ )
+
+ @property
+ def vocab_size(self):
+ return self._utf_vocab_size
+
+ def get_vocab(self):
+ vocab = {self.convert_ids_to_tokens(i): i for i in range(self.vocab_size + self.offset)}
+ vocab.update(self.added_tokens_encoder)
+ return vocab
+
+ def _tokenize(self, text: str) -> list[str]:
+ """Take as input a string and return a list of strings (tokens) for words/sub-words"""
+ tokens = [chr(i) for i in text.encode("utf-8")]
+ return tokens
+
+ def _convert_token_to_id(self, token):
+ """Converts a token (str) in an id using the vocab."""
+
+ if len(token) != 1:
+ token_id = None
+ else:
+ token_id = ord(token) + self.offset
+
+ return token_id
+
+ def _convert_id_to_token(self, index):
+ """Converts an index (integer) in a token (str) using the vocab."""
+ token = chr(index - self.offset)
+ return token
+
+ def convert_tokens_to_string(self, tokens: list[str]) -> str:
+ """Converts a sequence of tokens (string) in a single string."""
+ bstring = b""
+ for token in tokens:
+ if token in self.added_tokens_decoder:
+ added_token_obj = self.added_tokens_decoder[token]
+ tok_string = str(added_token_obj).encode("utf-8")
+ elif token in self.added_tokens_encoder:
+ tok_string = token.encode("utf-8")
+ else:
+ tok_string = token.encode("utf-8") # Assume general string token
+ bstring += tok_string
+ string = bstring.decode("utf-8", errors="ignore")
+ return string
+
+
+__all__ = ["DiaTokenizer"]
diff --git a/third_party/transformers/src/transformers/models/dialogpt/__init__.py b/third_party/transformers/src/transformers/models/dialogpt/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/third_party/transformers/src/transformers/models/dialogpt/convert_dialogpt_original_pytorch_checkpoint_to_pytorch.py b/third_party/transformers/src/transformers/models/dialogpt/convert_dialogpt_original_pytorch_checkpoint_to_pytorch.py
new file mode 100644
index 0000000000000000000000000000000000000000..03f38084cfbf7428678e0ec7b25d0fe2ae9dace1
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/dialogpt/convert_dialogpt_original_pytorch_checkpoint_to_pytorch.py
@@ -0,0 +1,46 @@
+# Copyright 2020 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import os
+
+import torch
+
+from transformers.utils import WEIGHTS_NAME
+
+
+DIALOGPT_MODELS = ["small", "medium", "large"]
+
+OLD_KEY = "lm_head.decoder.weight"
+NEW_KEY = "lm_head.weight"
+
+
+def convert_dialogpt_checkpoint(checkpoint_path: str, pytorch_dump_folder_path: str):
+ d = torch.load(checkpoint_path, weights_only=True)
+ d[NEW_KEY] = d.pop(OLD_KEY)
+ os.makedirs(pytorch_dump_folder_path, exist_ok=True)
+ torch.save(d, os.path.join(pytorch_dump_folder_path, WEIGHTS_NAME))
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--dialogpt_path", default=".", type=str)
+ args = parser.parse_args()
+ for MODEL in DIALOGPT_MODELS:
+ checkpoint_path = os.path.join(args.dialogpt_path, f"{MODEL}_ft.pkl")
+ pytorch_dump_folder_path = f"./DialoGPT-{MODEL}"
+ convert_dialogpt_checkpoint(
+ checkpoint_path,
+ pytorch_dump_folder_path,
+ )
diff --git a/third_party/transformers/src/transformers/models/dinov2_with_registers/__init__.py b/third_party/transformers/src/transformers/models/dinov2_with_registers/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..2d10027b6a3b6375235a6785df044e8f0ce5fb33
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/dinov2_with_registers/__init__.py
@@ -0,0 +1,27 @@
+# Copyright 2024 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_dinov2_with_registers import *
+ from .modeling_dinov2_with_registers import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/dinov2_with_registers/configuration_dinov2_with_registers.py b/third_party/transformers/src/transformers/models/dinov2_with_registers/configuration_dinov2_with_registers.py
new file mode 100644
index 0000000000000000000000000000000000000000..8125b30a952054eda44ae0c38f11f35ce3ee15c9
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/dinov2_with_registers/configuration_dinov2_with_registers.py
@@ -0,0 +1,94 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/dinov2_with_registers/modular_dinov2_with_registers.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_dinov2_with_registers.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2024 Meta Inc. and the HuggingFace Inc. team. All rights reserved.
+#
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+from huggingface_hub.dataclasses import strict
+
+from ...backbone_utils import BackboneConfigMixin
+from ...configuration_utils import PreTrainedConfig
+from ...utils import auto_docstring
+
+
+@auto_docstring(checkpoint="facebook/dinov2-with-registers-base")
+@strict
+class Dinov2WithRegistersConfig(BackboneConfigMixin, PreTrainedConfig):
+ r"""
+ layerscale_value (`float`, *optional*, defaults to 1.0):
+ Initial value to use for layer scale.
+ use_swiglu_ffn (`bool`, *optional*, defaults to `False`):
+ Whether to use the SwiGLU feedforward neural network.
+ num_register_tokens (`int`, *optional*, defaults to 4):
+ Number of register tokens to use.
+ apply_layernorm (`bool`, *optional*, defaults to `True`):
+ Whether to apply layer normalization to the feature maps in case the model is used as backbone.
+ reshape_hidden_states (`bool`, *optional*, defaults to `True`):
+ Whether to reshape the feature maps to 4D tensors of shape `(batch_size, hidden_size, height, width)` in
+ case the model is used as backbone. If `False`, the feature maps will be 3D tensors of shape `(batch_size,
+ seq_len, hidden_size)`.
+
+ Example:
+
+ ```python
+ >>> from transformers import Dinov2WithRegistersConfig, Dinov2WithRegistersModel
+
+ >>> # Initializing a Dinov2WithRegisters base style configuration
+ >>> configuration = Dinov2WithRegistersConfig()
+
+ >>> # Initializing a model (with random weights) from the base style configuration
+ >>> model = Dinov2WithRegistersModel(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "dinov2_with_registers"
+
+ hidden_size: int = 768
+ num_hidden_layers: int = 12
+ num_attention_heads: int = 12
+ mlp_ratio: int = 4
+ hidden_act: str = "gelu"
+ hidden_dropout_prob: float | int = 0.0
+ attention_probs_dropout_prob: float | int = 0.0
+ initializer_range: float = 0.02
+ layer_norm_eps: float = 1e-6
+ image_size: int | list[int] | tuple[int, int] = 224
+ patch_size: int | list[int] | tuple[int, int] = 16
+ num_channels: int = 3
+ qkv_bias: bool = True
+ layerscale_value: float = 1.0
+ drop_path_rate: float | int = 0.0
+ use_swiglu_ffn: bool = False
+ num_register_tokens: int = 4
+ _out_features: list[str] | None = None
+ _out_indices: list[int] | None = None
+ apply_layernorm: bool = True
+ reshape_hidden_states: bool = True
+
+ def __post_init__(self, **kwargs):
+ self.stage_names = ["stem"] + [f"stage{idx}" for idx in range(1, self.num_hidden_layers + 1)]
+ self.set_output_features_output_indices(
+ out_indices=kwargs.pop("out_indices", None), out_features=kwargs.pop("out_features", None)
+ )
+ super().__post_init__(**kwargs)
+
+
+__all__ = ["Dinov2WithRegistersConfig"]
diff --git a/third_party/transformers/src/transformers/models/dinov2_with_registers/convert_dinov2_with_registers_to_hf.py b/third_party/transformers/src/transformers/models/dinov2_with_registers/convert_dinov2_with_registers_to_hf.py
new file mode 100644
index 0000000000000000000000000000000000000000..c1c716ad1d958218d0314f2614f64d3de4036f64
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/dinov2_with_registers/convert_dinov2_with_registers_to_hf.py
@@ -0,0 +1,294 @@
+# Copyright 2024 The HuggingFace Inc. team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Convert DINOv2 with Registers checkpoints from the original repository.
+
+URL: https://github.com/facebookresearch/dinov2/tree/main
+"""
+
+import argparse
+import json
+from io import BytesIO
+from pathlib import Path
+
+import httpx
+import torch
+import torch.nn as nn
+from huggingface_hub import hf_hub_download
+from PIL import Image
+from torchvision import transforms
+
+from transformers import (
+ BitImageProcessor,
+ Dinov2WithRegistersConfig,
+ Dinov2WithRegistersForImageClassification,
+ Dinov2WithRegistersModel,
+)
+from transformers.image_utils import IMAGENET_DEFAULT_MEAN, IMAGENET_DEFAULT_STD, PILImageResampling
+from transformers.utils import logging
+
+
+logging.set_verbosity_info()
+logger = logging.get_logger(__name__)
+
+
+def get_dinov2_with_registers_config(model_name, image_classifier=False):
+ config = Dinov2WithRegistersConfig(image_size=518, patch_size=14)
+
+ # size of the architecture
+ if "vits" in model_name:
+ config.hidden_size = 384
+ config.num_attention_heads = 6
+ elif "vitb" in model_name:
+ pass
+ elif "vitl" in model_name:
+ config.hidden_size = 1024
+ config.num_hidden_layers = 24
+ config.num_attention_heads = 16
+ elif "vitg" in model_name:
+ config.use_swiglu_ffn = True
+ config.hidden_size = 1536
+ config.num_hidden_layers = 40
+ config.num_attention_heads = 24
+ else:
+ raise ValueError("Model not supported")
+
+ if image_classifier:
+ repo_id = "huggingface/label-files"
+ filename = "imagenet-1k-id2label.json"
+ config.num_labels = 1000
+ config.id2label = json.load(open(hf_hub_download(repo_id, filename, repo_type="dataset"), "r"))
+ config.id2label = {int(k): v for k, v in config.id2label.items()}
+
+ return config
+
+
+def create_rename_keys(config):
+ rename_keys = []
+ # fmt: off
+
+ # patch embedding layer
+ rename_keys.append(("cls_token", "embeddings.cls_token"))
+ rename_keys.append(("mask_token", "embeddings.mask_token"))
+ rename_keys.append(("pos_embed", "embeddings.position_embeddings"))
+ rename_keys.append(("register_tokens", "embeddings.register_tokens"))
+ rename_keys.append(("patch_embed.proj.weight", "embeddings.patch_embeddings.projection.weight"))
+ rename_keys.append(("patch_embed.proj.bias", "embeddings.patch_embeddings.projection.bias"))
+
+ for i in range(config.num_hidden_layers):
+ # layernorms
+ rename_keys.append((f"blocks.{i}.norm1.weight", f"encoder.layer.{i}.norm1.weight"))
+ rename_keys.append((f"blocks.{i}.norm1.bias", f"encoder.layer.{i}.norm1.bias"))
+ rename_keys.append((f"blocks.{i}.norm2.weight", f"encoder.layer.{i}.norm2.weight"))
+ rename_keys.append((f"blocks.{i}.norm2.bias", f"encoder.layer.{i}.norm2.bias"))
+ # MLP
+ if config.use_swiglu_ffn:
+ rename_keys.append((f"blocks.{i}.mlp.w12.weight", f"encoder.layer.{i}.mlp.w12.weight"))
+ rename_keys.append((f"blocks.{i}.mlp.w12.bias", f"encoder.layer.{i}.mlp.w12.bias"))
+ rename_keys.append((f"blocks.{i}.mlp.w3.weight", f"encoder.layer.{i}.mlp.w3.weight"))
+ rename_keys.append((f"blocks.{i}.mlp.w3.bias", f"encoder.layer.{i}.mlp.w3.bias"))
+ else:
+ rename_keys.append((f"blocks.{i}.mlp.fc1.weight", f"encoder.layer.{i}.mlp.fc1.weight"))
+ rename_keys.append((f"blocks.{i}.mlp.fc1.bias", f"encoder.layer.{i}.mlp.fc1.bias"))
+ rename_keys.append((f"blocks.{i}.mlp.fc2.weight", f"encoder.layer.{i}.mlp.fc2.weight"))
+ rename_keys.append((f"blocks.{i}.mlp.fc2.bias", f"encoder.layer.{i}.mlp.fc2.bias"))
+ # layerscale
+ rename_keys.append((f"blocks.{i}.ls1.gamma", f"encoder.layer.{i}.layer_scale1.lambda1"))
+ rename_keys.append((f"blocks.{i}.ls2.gamma", f"encoder.layer.{i}.layer_scale2.lambda1"))
+ # attention projection layer
+ rename_keys.append((f"blocks.{i}.attn.proj.weight", f"encoder.layer.{i}.attention.output.dense.weight"))
+ rename_keys.append((f"blocks.{i}.attn.proj.bias", f"encoder.layer.{i}.attention.output.dense.bias"))
+
+ # final layernorm
+ rename_keys.append(("norm.weight", "layernorm.weight"))
+ rename_keys.append(("norm.bias", "layernorm.bias"))
+
+ # fmt: on
+ return rename_keys
+
+
+def rename_key(dct, old, new):
+ val = dct.pop(old)
+ dct[new] = val
+
+
+# we split up the matrix of each encoder layer into queries, keys and values
+def read_in_q_k_v(state_dict, config):
+ for i in range(config.num_hidden_layers):
+ # read in weights + bias of input projection layer (in timm, this is a single matrix + bias)
+ in_proj_weight = state_dict.pop(f"blocks.{i}.attn.qkv.weight")
+ in_proj_bias = state_dict.pop(f"blocks.{i}.attn.qkv.bias")
+ # next, add query, keys and values (in that order) to the state dict
+ state_dict[f"encoder.layer.{i}.attention.attention.query.weight"] = in_proj_weight[: config.hidden_size, :]
+ state_dict[f"encoder.layer.{i}.attention.attention.query.bias"] = in_proj_bias[: config.hidden_size]
+ state_dict[f"encoder.layer.{i}.attention.attention.key.weight"] = in_proj_weight[
+ config.hidden_size : config.hidden_size * 2, :
+ ]
+ state_dict[f"encoder.layer.{i}.attention.attention.key.bias"] = in_proj_bias[
+ config.hidden_size : config.hidden_size * 2
+ ]
+ state_dict[f"encoder.layer.{i}.attention.attention.value.weight"] = in_proj_weight[-config.hidden_size :, :]
+ state_dict[f"encoder.layer.{i}.attention.attention.value.bias"] = in_proj_bias[-config.hidden_size :]
+
+
+# We will verify our results on an image of cute cats
+def prepare_img():
+ url = "http://images.cocodataset.org/val2017/000000039769.jpg"
+ with httpx.stream("GET", url) as response:
+ image = Image.open(BytesIO(response.read())).convert("RGB")
+ return image
+
+
+@torch.no_grad()
+def convert_dinov2_with_registers_checkpoint(model_name, pytorch_dump_folder_path, push_to_hub=False):
+ """
+ Copy/paste/tweak model's weights to our Dinov2WithRegisters structure.
+ """
+
+ # define default Dinov2WithRegisters configuration
+ image_classifier = "1layer" in model_name
+ config = get_dinov2_with_registers_config(model_name, image_classifier=image_classifier)
+
+ # load original model from torch hub
+ original_model = torch.hub.load("facebookresearch/dinov2", model_name.replace("_1layer", ""))
+ original_model.eval()
+
+ # load state_dict of original model, remove and rename some keys
+ state_dict = original_model.state_dict()
+ rename_keys = create_rename_keys(config)
+ for src, dest in rename_keys:
+ rename_key(state_dict, src, dest)
+ read_in_q_k_v(state_dict, config)
+
+ for key, val in state_dict.copy().items():
+ val = state_dict.pop(key)
+ if "w12" in key:
+ key = key.replace("w12", "weights_in")
+ if "w3" in key:
+ key = key.replace("w3", "weights_out")
+ state_dict[key] = val
+
+ # load HuggingFace model
+ if image_classifier:
+ model = Dinov2WithRegistersForImageClassification(config).eval()
+ model.dinov2_with_registers.load_state_dict(state_dict)
+ model_name_to_classifier_dict_url = {
+ "dinov2_vits14_reg_1layer": "https://dl.fbaipublicfiles.com/dinov2/dinov2_vits14/dinov2_vits14_reg4_linear_head.pth",
+ "dinov2_vitb14_reg_1layer": "https://dl.fbaipublicfiles.com/dinov2/dinov2_vitb14/dinov2_vitb14_reg4_linear_head.pth",
+ "dinov2_vitl14_reg_1layer": "https://dl.fbaipublicfiles.com/dinov2/dinov2_vitl14/dinov2_vitl14_reg4_linear_head.pth",
+ "dinov2_vitg14_reg_1layer": "https://dl.fbaipublicfiles.com/dinov2/dinov2_vitg14/dinov2_vitg14_reg4_linear_head.pth",
+ }
+ url = model_name_to_classifier_dict_url[model_name]
+ classifier_state_dict = torch.hub.load_state_dict_from_url(url, map_location="cpu")
+ model.classifier.weight = nn.Parameter(classifier_state_dict["weight"])
+ model.classifier.bias = nn.Parameter(classifier_state_dict["bias"])
+ else:
+ model = Dinov2WithRegistersModel(config).eval()
+ model.load_state_dict(state_dict)
+
+ # load image
+ image = prepare_img()
+
+ # preprocess image
+ transformations = transforms.Compose(
+ [
+ transforms.Resize(256, interpolation=transforms.InterpolationMode.BICUBIC),
+ transforms.CenterCrop(224),
+ transforms.ToTensor(),
+ transforms.Normalize(
+ mean=IMAGENET_DEFAULT_MEAN, # these are RGB mean+std values
+ std=IMAGENET_DEFAULT_STD, # across a large photo dataset.
+ ),
+ ]
+ )
+
+ original_pixel_values = transformations(image).unsqueeze(0) # insert batch dimension
+
+ processor = BitImageProcessor(
+ size={"shortest_edge": 256},
+ resample=PILImageResampling.BICUBIC,
+ image_mean=IMAGENET_DEFAULT_MEAN,
+ image_std=IMAGENET_DEFAULT_STD,
+ )
+ pixel_values = processor(image, return_tensors="pt").pixel_values
+
+ assert torch.allclose(original_pixel_values, pixel_values)
+
+ with torch.no_grad():
+ outputs = model(pixel_values, output_hidden_states=True)
+ original_outputs = original_model(pixel_values)
+
+ # assert values
+ if image_classifier:
+ print("Predicted class:")
+ class_idx = outputs.logits.argmax(-1).item()
+ print(model.config.id2label[class_idx])
+ else:
+ assert outputs.last_hidden_state[:, 0].shape == original_outputs.shape
+ assert torch.allclose(outputs.last_hidden_state[:, 0], original_outputs, atol=1e-3)
+ print("Looks ok!")
+
+ if pytorch_dump_folder_path is not None:
+ Path(pytorch_dump_folder_path).mkdir(exist_ok=True)
+ print(f"Saving model {model_name} to {pytorch_dump_folder_path}")
+ model.save_pretrained(pytorch_dump_folder_path)
+ print(f"Saving image processor to {pytorch_dump_folder_path}")
+ processor.save_pretrained(pytorch_dump_folder_path)
+
+ if push_to_hub:
+ model_name_to_hf_name = {
+ "dinov2_vits14_reg": "dinov2-with-registers-small",
+ "dinov2_vitb14_reg": "dinov2-with-registers-base",
+ "dinov2_vitl14_reg": "dinov2-with-registers-large",
+ "dinov2_vitg14_reg": "dinov2-with-registers-giant",
+ "dinov2_vits14_reg_1layer": "dinov2-with-registers-small-imagenet1k-1-layer",
+ "dinov2_vitb14_reg_1layer": "dinov2-with-registers-base-imagenet1k-1-layer",
+ "dinov2_vitl14_reg_1layer": "dinov2-with-registers-large-imagenet1k-1-layer",
+ "dinov2_vitg14_reg_1layer": "dinov2-with-registers-giant-imagenet1k-1-layer",
+ }
+
+ name = model_name_to_hf_name[model_name]
+ model.push_to_hub(f"nielsr/{name}")
+ processor.push_to_hub(f"nielsr/{name}")
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ # Required parameters
+ parser.add_argument(
+ "--model_name",
+ default="dinov2_vits14_reg",
+ type=str,
+ choices=[
+ "dinov2_vits14_reg",
+ "dinov2_vitb14_reg",
+ "dinov2_vitl14_reg",
+ "dinov2_vitg14_reg",
+ "dinov2_vits14_reg_1layer",
+ "dinov2_vitb14_reg_1layer",
+ "dinov2_vitl14_reg_1layer",
+ "dinov2_vitg14_reg_1layer",
+ ],
+ help="Name of the model you'd like to convert.",
+ )
+ parser.add_argument(
+ "--pytorch_dump_folder_path", default=None, type=str, help="Path to the output PyTorch model directory."
+ )
+ parser.add_argument(
+ "--push_to_hub",
+ action="store_true",
+ help="Whether or not to push the converted model to the Hugging Face hub.",
+ )
+
+ args = parser.parse_args()
+ convert_dinov2_with_registers_checkpoint(args.model_name, args.pytorch_dump_folder_path, args.push_to_hub)
diff --git a/third_party/transformers/src/transformers/models/dinov2_with_registers/modeling_dinov2_with_registers.py b/third_party/transformers/src/transformers/models/dinov2_with_registers/modeling_dinov2_with_registers.py
new file mode 100644
index 0000000000000000000000000000000000000000..8e81fd8ddb4f805ca68d88cba4d4c968d4a7b580
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/dinov2_with_registers/modeling_dinov2_with_registers.py
@@ -0,0 +1,653 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/dinov2_with_registers/modular_dinov2_with_registers.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_dinov2_with_registers.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2024 Meta Inc. and the HuggingFace Inc. team. All rights reserved.
+#
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import collections.abc
+from collections.abc import Callable
+
+import torch
+from torch import nn
+
+from ... import initialization as init
+from ...activations import ACT2FN
+from ...backbone_utils import BackboneMixin, filter_output_hidden_states
+from ...modeling_layers import GradientCheckpointingLayer
+from ...modeling_outputs import BackboneOutput, BaseModelOutput, BaseModelOutputWithPooling, ImageClassifierOutput
+from ...modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
+from ...processing_utils import Unpack
+from ...utils import TransformersKwargs, auto_docstring, torch_int
+from ...utils.generic import can_return_tuple, merge_with_config_defaults
+from ...utils.output_capturing import capture_outputs
+from .configuration_dinov2_with_registers import Dinov2WithRegistersConfig
+
+
+class Dinov2WithRegistersPatchEmbeddings(nn.Module):
+ """
+ This class turns `pixel_values` of shape `(batch_size, num_channels, height, width)` into the initial
+ `hidden_states` (patch embeddings) of shape `(batch_size, seq_length, hidden_size)` to be consumed by a
+ Transformer.
+ """
+
+ def __init__(self, config):
+ super().__init__()
+ image_size, patch_size = config.image_size, config.patch_size
+ num_channels, hidden_size = config.num_channels, config.hidden_size
+
+ image_size = image_size if isinstance(image_size, collections.abc.Iterable) else (image_size, image_size)
+ patch_size = patch_size if isinstance(patch_size, collections.abc.Iterable) else (patch_size, patch_size)
+ num_patches = (image_size[1] // patch_size[1]) * (image_size[0] // patch_size[0])
+ self.image_size = image_size
+ self.patch_size = patch_size
+ self.num_channels = num_channels
+ self.num_patches = num_patches
+
+ self.projection = nn.Conv2d(num_channels, hidden_size, kernel_size=patch_size, stride=patch_size)
+
+ def forward(self, pixel_values: torch.Tensor) -> torch.Tensor:
+ num_channels = pixel_values.shape[1]
+ if num_channels != self.num_channels:
+ raise ValueError(
+ "Make sure that the channel dimension of the pixel values match with the one set in the configuration."
+ f" Expected {self.num_channels} but got {num_channels}."
+ )
+ embeddings = self.projection(pixel_values).flatten(2).transpose(1, 2)
+ return embeddings
+
+
+class Dinov2WithRegistersEmbeddings(nn.Module):
+ """
+ Construct the CLS token, mask token, register tokens, position and patch embeddings.
+ """
+
+ def __init__(self, config: Dinov2WithRegistersConfig) -> None:
+ super().__init__()
+
+ self.cls_token = nn.Parameter(torch.randn(1, 1, config.hidden_size))
+ self.mask_token = nn.Parameter(torch.zeros(1, config.hidden_size))
+ self.register_tokens = nn.Parameter(torch.zeros(1, config.num_register_tokens, config.hidden_size))
+ self.patch_embeddings = Dinov2WithRegistersPatchEmbeddings(config)
+ num_patches = self.patch_embeddings.num_patches
+ self.position_embeddings = nn.Parameter(torch.randn(1, num_patches + 1, config.hidden_size))
+ self.dropout = nn.Dropout(config.hidden_dropout_prob)
+ self.patch_size = config.patch_size
+ self.config = config
+
+ def interpolate_pos_encoding(self, embeddings: torch.Tensor, height: int, width: int) -> torch.Tensor:
+ """
+ This method allows to interpolate the pre-trained position encodings, to be able to use the model on higher
+ resolution images. This implementation supports torch.jit tracing while maintaining backwards compatibility
+ with the original implementation.
+
+ Adapted from:
+ - https://github.com/facebookresearch/dino/blob/main/vision_transformer.py
+ - https://github.com/facebookresearch/dinov2/blob/main/dinov2/models/vision_transformer.py
+ """
+ num_patches = embeddings.shape[1] - 1
+ num_positions = self.position_embeddings.shape[1] - 1
+
+ # Skip interpolation for matching dimensions (unless tracing)
+ if not torch.jit.is_tracing() and num_patches == num_positions and height == width:
+ return self.position_embeddings
+
+ # Handle class token and patch embeddings separately
+ class_pos_embed = self.position_embeddings[:, 0]
+ patch_pos_embed = self.position_embeddings[:, 1:]
+ dim = embeddings.shape[-1]
+
+ # Calculate new dimensions
+ height = height // self.config.patch_size
+ width = width // self.config.patch_size
+
+ # Reshape for interpolation
+ sqrt_num_positions = torch_int(num_positions**0.5)
+ patch_pos_embed = patch_pos_embed.reshape(1, sqrt_num_positions, sqrt_num_positions, dim)
+ patch_pos_embed = patch_pos_embed.permute(0, 3, 1, 2)
+
+ # Store original dtype for restoration after interpolation
+ target_dtype = patch_pos_embed.dtype
+
+ # Interpolate at float32 precision
+ patch_pos_embed = nn.functional.interpolate(
+ patch_pos_embed.to(dtype=torch.float32),
+ size=(torch_int(height), torch_int(width)), # Explicit size instead of scale_factor
+ mode="bicubic",
+ align_corners=False,
+ antialias=True,
+ ).to(dtype=target_dtype)
+
+ # Validate output dimensions if not tracing
+ if not torch.jit.is_tracing():
+ if int(height) != patch_pos_embed.shape[-2] or int(width) != patch_pos_embed.shape[-1]:
+ raise ValueError("Width or height does not match with the interpolated position embeddings")
+
+ # Reshape back to original format
+ patch_pos_embed = patch_pos_embed.permute(0, 2, 3, 1).view(1, -1, dim)
+
+ # Combine class and patch embeddings
+ return torch.cat((class_pos_embed.unsqueeze(0), patch_pos_embed), dim=1)
+
+ def forward(self, pixel_values: torch.Tensor, bool_masked_pos: torch.Tensor | None = None) -> torch.Tensor:
+ batch_size, _, height, width = pixel_values.shape
+ target_dtype = self.patch_embeddings.projection.weight.dtype
+ embeddings = self.patch_embeddings(pixel_values.to(dtype=target_dtype))
+
+ if bool_masked_pos is not None:
+ embeddings = torch.where(
+ bool_masked_pos.unsqueeze(-1), self.mask_token.to(embeddings.dtype).unsqueeze(0), embeddings
+ )
+
+ # add the [CLS] token to the embedded patch tokens
+ cls_tokens = self.cls_token.expand(batch_size, -1, -1)
+ embeddings = torch.cat((cls_tokens, embeddings), dim=1)
+
+ # add positional encoding to each token
+ embeddings = embeddings + self.interpolate_pos_encoding(embeddings, height, width)
+
+ # add register tokens
+ embeddings = torch.cat(
+ (embeddings[:, :1], self.register_tokens.expand(embeddings.shape[0], -1, -1), embeddings[:, 1:]), dim=1
+ )
+
+ embeddings = self.dropout(embeddings)
+
+ return embeddings
+
+
+def eager_attention_forward(
+ module: nn.Module,
+ query: torch.Tensor,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ attention_mask: torch.Tensor | None,
+ scaling: float | None = None,
+ dropout: float = 0.0,
+ **kwargs: Unpack[TransformersKwargs],
+):
+ if scaling is None:
+ scaling = query.size(-1) ** -0.5
+
+ # Take the dot product between "query" and "key" to get the raw attention scores.
+ attn_weights = torch.matmul(query, key.transpose(2, 3)) * scaling
+
+ if attention_mask is not None:
+ attn_weights = attn_weights + attention_mask
+
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1)
+ attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
+
+ attn_output = torch.matmul(attn_weights, value)
+ attn_output = attn_output.transpose(1, 2).contiguous()
+
+ return attn_output, attn_weights
+
+
+class Dinov2WithRegistersSelfAttention(nn.Module):
+ def __init__(self, config: Dinov2WithRegistersConfig):
+ super().__init__()
+ if config.hidden_size % config.num_attention_heads != 0 and not hasattr(config, "embedding_size"):
+ raise ValueError(
+ f"The hidden size {config.hidden_size} is not a multiple of the number of attention "
+ f"heads {config.num_attention_heads}."
+ )
+
+ self.config = config
+ self.num_attention_heads = config.num_attention_heads
+ self.attention_head_size = int(config.hidden_size / config.num_attention_heads)
+ self.all_head_size = self.num_attention_heads * self.attention_head_size
+ self.dropout_prob = config.attention_probs_dropout_prob
+ self.scaling = self.attention_head_size**-0.5
+ self.is_causal = False
+
+ self.query = nn.Linear(config.hidden_size, self.all_head_size, bias=config.qkv_bias)
+ self.key = nn.Linear(config.hidden_size, self.all_head_size, bias=config.qkv_bias)
+ self.value = nn.Linear(config.hidden_size, self.all_head_size, bias=config.qkv_bias)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple[torch.Tensor, torch.Tensor]:
+ batch_size = hidden_states.shape[0]
+ new_shape = batch_size, -1, self.num_attention_heads, self.attention_head_size
+
+ key_layer = self.key(hidden_states).view(*new_shape).transpose(1, 2)
+ value_layer = self.value(hidden_states).view(*new_shape).transpose(1, 2)
+ query_layer = self.query(hidden_states).view(*new_shape).transpose(1, 2)
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ context_layer, attention_probs = attention_interface(
+ self,
+ query_layer,
+ key_layer,
+ value_layer,
+ None,
+ is_causal=self.is_causal,
+ scaling=self.scaling,
+ dropout=0.0 if not self.training else self.dropout_prob,
+ **kwargs,
+ )
+
+ new_context_layer_shape = context_layer.size()[:-2] + (self.all_head_size,)
+ context_layer = context_layer.reshape(new_context_layer_shape)
+
+ return context_layer, attention_probs
+
+
+class Dinov2WithRegistersSelfOutput(nn.Module):
+ """
+ The residual connection is defined in Dinov2WithRegistersLayer instead of here (as is the case with other models), due to the
+ layernorm applied before each block.
+ """
+
+ def __init__(self, config: Dinov2WithRegistersConfig):
+ super().__init__()
+ self.dense = nn.Linear(config.hidden_size, config.hidden_size)
+ self.dropout = nn.Dropout(config.hidden_dropout_prob)
+
+ def forward(self, hidden_states: torch.Tensor, input_tensor: torch.Tensor) -> torch.Tensor:
+ hidden_states = self.dense(hidden_states)
+ hidden_states = self.dropout(hidden_states)
+ return hidden_states
+
+
+class Dinov2WithRegistersAttention(nn.Module):
+ def __init__(self, config: Dinov2WithRegistersConfig):
+ super().__init__()
+ self.attention = Dinov2WithRegistersSelfAttention(config)
+ self.output = Dinov2WithRegistersSelfOutput(config)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> torch.Tensor:
+ self_attn_output, _ = self.attention(hidden_states, **kwargs)
+ output = self.output(self_attn_output, hidden_states)
+ return output
+
+
+class Dinov2WithRegistersLayerScale(nn.Module):
+ def __init__(self, config) -> None:
+ super().__init__()
+ self.lambda1 = nn.Parameter(config.layerscale_value * torch.ones(config.hidden_size))
+
+ def forward(self, hidden_state: torch.Tensor) -> torch.Tensor:
+ return hidden_state * self.lambda1
+
+
+def drop_path(input: torch.Tensor, drop_prob: float = 0.0, training: bool = False) -> torch.Tensor:
+ """
+ Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).
+
+ """
+ if drop_prob == 0.0 or not training:
+ return input
+ keep_prob = 1 - drop_prob
+ shape = (input.shape[0],) + (1,) * (input.ndim - 1) # work with diff dim tensors, not just 2D ConvNets
+ random_tensor = keep_prob + torch.rand(shape, dtype=input.dtype, device=input.device)
+ random_tensor.floor_() # binarize
+ output = input.div(keep_prob) * random_tensor
+ return output
+
+
+class Dinov2WithRegistersDropPath(nn.Module):
+ """Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks)."""
+
+ def __init__(self, drop_prob: float | None = None) -> None:
+ super().__init__()
+ self.drop_prob = drop_prob
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ return drop_path(hidden_states, self.drop_prob, self.training)
+
+ def extra_repr(self) -> str:
+ return f"p={self.drop_prob}"
+
+
+class Dinov2WithRegistersMLP(nn.Module):
+ def __init__(self, config) -> None:
+ super().__init__()
+ in_features = out_features = config.hidden_size
+ hidden_features = int(config.hidden_size * config.mlp_ratio)
+ self.fc1 = nn.Linear(in_features, hidden_features, bias=True)
+ if isinstance(config.hidden_act, str):
+ self.activation = ACT2FN[config.hidden_act]
+ else:
+ self.activation = config.hidden_act
+ self.fc2 = nn.Linear(hidden_features, out_features, bias=True)
+
+ def forward(self, hidden_state: torch.Tensor) -> torch.Tensor:
+ hidden_state = self.fc1(hidden_state)
+ hidden_state = self.activation(hidden_state)
+ hidden_state = self.fc2(hidden_state)
+ return hidden_state
+
+
+class Dinov2WithRegistersSwiGLUFFN(nn.Module):
+ def __init__(self, config) -> None:
+ super().__init__()
+ in_features = out_features = config.hidden_size
+ hidden_features = int(config.hidden_size * config.mlp_ratio)
+ hidden_features = (int(hidden_features * 2 / 3) + 7) // 8 * 8
+
+ self.weights_in = nn.Linear(in_features, 2 * hidden_features, bias=True)
+ self.weights_out = nn.Linear(hidden_features, out_features, bias=True)
+
+ def forward(self, hidden_state: torch.Tensor) -> torch.Tensor:
+ hidden_state = self.weights_in(hidden_state)
+ x1, x2 = hidden_state.chunk(2, dim=-1)
+ hidden = nn.functional.silu(x1) * x2
+ return self.weights_out(hidden)
+
+
+class Dinov2WithRegistersLayer(GradientCheckpointingLayer):
+ """This corresponds to the Block class in the original implementation."""
+
+ def __init__(self, config: Dinov2WithRegistersConfig) -> None:
+ super().__init__()
+
+ self.norm1 = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
+ self.attention = Dinov2WithRegistersAttention(config)
+ self.layer_scale1 = Dinov2WithRegistersLayerScale(config)
+ self.drop_path = (
+ Dinov2WithRegistersDropPath(config.drop_path_rate) if config.drop_path_rate > 0.0 else nn.Identity()
+ )
+
+ self.norm2 = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
+
+ if config.use_swiglu_ffn:
+ self.mlp = Dinov2WithRegistersSwiGLUFFN(config)
+ else:
+ self.mlp = Dinov2WithRegistersMLP(config)
+ self.layer_scale2 = Dinov2WithRegistersLayerScale(config)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ ) -> torch.Tensor:
+ hidden_states_norm = self.norm1(hidden_states)
+ self_attention_output = self.attention(hidden_states_norm)
+ self_attention_output = self.layer_scale1(self_attention_output)
+
+ # first residual connection
+ hidden_states = self.drop_path(self_attention_output) + hidden_states
+
+ # in Dinov2WithRegisters, layernorm is also applied after self-attention
+ layer_output = self.norm2(hidden_states)
+ layer_output = self.mlp(layer_output)
+ layer_output = self.layer_scale2(layer_output)
+
+ # second residual connection
+ layer_output = self.drop_path(layer_output) + hidden_states
+
+ return layer_output
+
+
+@auto_docstring
+class Dinov2WithRegistersPreTrainedModel(PreTrainedModel):
+ config: Dinov2WithRegistersConfig
+ base_model_prefix = "dinov2_with_registers"
+ main_input_name = "pixel_values"
+ input_modalities = ("image",)
+ supports_gradient_checkpointing = True
+ _no_split_modules = ["Dinov2WithRegistersLayer"]
+ _supports_sdpa = True
+ _supports_flash_attn = True
+ _supports_flex_attn = True
+ _supports_attention_backend = True
+ _can_record_outputs = {
+ "hidden_states": Dinov2WithRegistersLayer,
+ "attentions": Dinov2WithRegistersSelfAttention,
+ }
+
+ @torch.no_grad()
+ def _init_weights(self, module: nn.Linear | nn.Conv2d | nn.LayerNorm) -> None:
+ """Initialize the weights"""
+ if isinstance(module, (nn.Linear, nn.Conv2d)):
+ init.trunc_normal_(module.weight, mean=0.0, std=self.config.initializer_range)
+ if module.bias is not None:
+ init.zeros_(module.bias)
+ elif isinstance(module, nn.LayerNorm):
+ init.zeros_(module.bias)
+ init.ones_(module.weight)
+ elif isinstance(module, Dinov2WithRegistersEmbeddings):
+ init.trunc_normal_(module.position_embeddings, mean=0.0, std=self.config.initializer_range)
+ init.trunc_normal_(module.cls_token, mean=0.0, std=self.config.initializer_range)
+ init.zeros_(module.mask_token)
+ init.zeros_(module.register_tokens)
+ elif isinstance(module, Dinov2WithRegistersLayerScale): # noqa: F821
+ init.constant_(module.lambda1, self.config.layerscale_value)
+
+
+class Dinov2WithRegistersEncoder(Dinov2WithRegistersPreTrainedModel):
+ def __init__(self, config: Dinov2WithRegistersConfig):
+ super().__init__(config)
+ self.layer = nn.ModuleList([Dinov2WithRegistersLayer(config) for _ in range(config.num_hidden_layers)])
+ self.post_init()
+
+ @merge_with_config_defaults
+ @capture_outputs(tie_last_hidden_states=False)
+ def forward(self, hidden_states: torch.Tensor, **kwargs: Unpack[TransformersKwargs]) -> BaseModelOutput:
+ for layer_module in self.layer:
+ hidden_states = layer_module(hidden_states)
+
+ return BaseModelOutput(last_hidden_state=hidden_states)
+
+
+@auto_docstring
+class Dinov2WithRegistersModel(Dinov2WithRegistersPreTrainedModel):
+ def __init__(self, config: Dinov2WithRegistersConfig):
+ super().__init__(config)
+ self.config = config
+
+ self.embeddings = Dinov2WithRegistersEmbeddings(config)
+ self.encoder = Dinov2WithRegistersEncoder(config)
+
+ self.layernorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ def get_input_embeddings(self) -> Dinov2WithRegistersPatchEmbeddings:
+ return self.embeddings.patch_embeddings
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ pixel_values: torch.Tensor | None = None,
+ bool_masked_pos: torch.Tensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> BaseModelOutputWithPooling:
+ r"""
+ bool_masked_pos (`torch.BoolTensor` of shape `(batch_size, sequence_length)`):
+ Boolean masked positions. Indicates which patches are masked (1) and which aren't (0). Only relevant for
+ pre-training.
+ """
+ if pixel_values is None:
+ raise ValueError("You have to specify pixel_values")
+
+ embedding_output = self.embeddings(pixel_values, bool_masked_pos=bool_masked_pos)
+
+ encoder_outputs: BaseModelOutput = self.encoder(embedding_output, **kwargs)
+ sequence_output = encoder_outputs.last_hidden_state
+ sequence_output = self.layernorm(sequence_output)
+ pooled_output = sequence_output[:, 0, :]
+
+ return BaseModelOutputWithPooling(
+ last_hidden_state=sequence_output,
+ pooler_output=pooled_output,
+ hidden_states=encoder_outputs.hidden_states,
+ attentions=encoder_outputs.attentions,
+ )
+
+
+@auto_docstring(
+ custom_intro="""
+ Dinov2WithRegisters Model transformer with an image classification head on top (a linear layer on top of the final hidden state
+ of the [CLS] token) e.g. for ImageNet.
+ """
+)
+class Dinov2WithRegistersForImageClassification(Dinov2WithRegistersPreTrainedModel):
+ def __init__(self, config: Dinov2WithRegistersConfig) -> None:
+ super().__init__(config)
+
+ self.num_labels = config.num_labels
+ self.dinov2_with_registers = Dinov2WithRegistersModel(config)
+
+ # Classifier head
+ self.classifier = (
+ nn.Linear(config.hidden_size * 2, config.num_labels) if config.num_labels > 0 else nn.Identity()
+ )
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ pixel_values: torch.Tensor | None = None,
+ labels: torch.Tensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> ImageClassifierOutput:
+ r"""
+ labels (`torch.LongTensor` of shape `(batch_size,)`, *optional*):
+ Labels for computing the image classification/regression loss. Indices should be in `[0, ...,
+ config.num_labels - 1]`. If `config.num_labels == 1` a regression loss is computed (Mean-Square loss), If
+ `config.num_labels > 1` a classification loss is computed (Cross-Entropy).
+ """
+
+ outputs: BaseModelOutputWithPooling = self.dinov2_with_registers(pixel_values, **kwargs)
+ sequence_output = outputs.last_hidden_state # batch_size, sequence_length, hidden_size
+
+ cls_token = sequence_output[:, 0]
+ # cls and register tokens should not be included in patch tokens variable
+ patch_tokens = sequence_output[:, 1 + self.config.num_register_tokens :]
+
+ linear_input = torch.cat([cls_token, patch_tokens.mean(dim=1)], dim=1)
+ logits = self.classifier(linear_input)
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(labels, logits, self.config, **kwargs)
+
+ return ImageClassifierOutput(
+ loss=loss,
+ logits=logits,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ )
+
+
+@auto_docstring(
+ custom_intro="""
+ Dinov2WithRegisters backbone, to be used with frameworks like DETR and MaskFormer.
+ """
+)
+class Dinov2WithRegistersBackbone(BackboneMixin, Dinov2WithRegistersPreTrainedModel):
+ def __init__(self, config):
+ super().__init__(config)
+ self.num_features = [config.hidden_size for _ in range(config.num_hidden_layers + 1)]
+ self.embeddings = Dinov2WithRegistersEmbeddings(config)
+ self.encoder = Dinov2WithRegistersEncoder(config)
+
+ self.layernorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
+
+ self.num_register_tokens = config.num_register_tokens
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ def get_input_embeddings(self) -> Dinov2WithRegistersPatchEmbeddings:
+ return self.embeddings.patch_embeddings
+
+ @can_return_tuple
+ @filter_output_hidden_states
+ @auto_docstring
+ def forward(
+ self,
+ pixel_values: torch.Tensor,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> BackboneOutput:
+ r"""
+ Examples:
+
+ ```python
+ >>> from transformers import AutoImageProcessor, AutoBackbone
+ >>> import torch
+ >>> from PIL import Image
+ >>> import httpx
+ >>> from io import BytesIO
+
+ >>> url = "http://images.cocodataset.org/val2017/000000039769.jpg"
+ >>> with httpx.stream("GET", url) as response:
+ ... image = Image.open(BytesIO(response.read()))
+
+ >>> processor = AutoImageProcessor.from_pretrained("facebook/dinov2-with-registers-base")
+ >>> model = AutoBackbone.from_pretrained(
+ ... "facebook/dinov2-with-registers-base", out_features=["stage2", "stage5", "stage8", "stage11"]
+ ... )
+
+ >>> inputs = processor(image, return_tensors="pt")
+
+ >>> outputs = model(**inputs)
+ >>> feature_maps = outputs.feature_maps
+ >>> list(feature_maps[-1].shape)
+ [1, 768, 16, 16]
+ ```"""
+ kwargs["output_hidden_states"] = True # required to extract layers for the stages
+
+ embedding_output = self.embeddings(pixel_values)
+ output: BaseModelOutput = self.encoder(embedding_output, **kwargs)
+ hidden_states = output.hidden_states
+
+ feature_maps = []
+ for stage, hidden_state in zip(self.stage_names, hidden_states):
+ if stage in self.out_features:
+ if self.config.apply_layernorm:
+ hidden_state = self.layernorm(hidden_state)
+ if self.config.reshape_hidden_states:
+ hidden_state = hidden_state[:, 1 + self.num_register_tokens :]
+ # this was actually a bug in the original implementation that we copied here,
+ # cause normally the order is height, width
+ batch_size, _, height, width = pixel_values.shape
+ patch_size = self.config.patch_size
+ hidden_state = hidden_state.reshape(batch_size, height // patch_size, width // patch_size, -1)
+ hidden_state = hidden_state.permute(0, 3, 1, 2).contiguous()
+ feature_maps.append(hidden_state)
+
+ return BackboneOutput(
+ feature_maps=tuple(feature_maps),
+ hidden_states=hidden_states,
+ attentions=output.attentions,
+ )
+
+
+__all__ = [
+ "Dinov2WithRegistersPreTrainedModel",
+ "Dinov2WithRegistersModel",
+ "Dinov2WithRegistersForImageClassification",
+ "Dinov2WithRegistersBackbone",
+]
diff --git a/third_party/transformers/src/transformers/models/dinov2_with_registers/modular_dinov2_with_registers.py b/third_party/transformers/src/transformers/models/dinov2_with_registers/modular_dinov2_with_registers.py
new file mode 100644
index 0000000000000000000000000000000000000000..ee0ec16e2fdb77320da217583673de7ba40e9629
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/dinov2_with_registers/modular_dinov2_with_registers.py
@@ -0,0 +1,353 @@
+# Copyright 2024 Meta Inc. and the HuggingFace Inc. team. All rights reserved.
+#
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import torch
+from huggingface_hub.dataclasses import strict
+from torch import nn
+
+from ....transformers.models.dinov2.modeling_dinov2 import (
+ Dinov2Backbone,
+ Dinov2Encoder,
+ Dinov2ForImageClassification,
+ Dinov2Model,
+ Dinov2PatchEmbeddings,
+ Dinov2PreTrainedModel,
+)
+from ... import initialization as init
+from ...backbone_utils import BackboneConfigMixin
+from ...configuration_utils import PreTrainedConfig
+from ...modeling_outputs import BackboneOutput, BaseModelOutput, BaseModelOutputWithPooling, ImageClassifierOutput
+from ...processing_utils import Unpack
+from ...utils import TransformersKwargs, auto_docstring, logging, torch_int
+
+
+logger = logging.get_logger(__name__)
+
+
+@auto_docstring(checkpoint="facebook/dinov2-with-registers-base")
+@strict
+class Dinov2WithRegistersConfig(BackboneConfigMixin, PreTrainedConfig):
+ r"""
+ layerscale_value (`float`, *optional*, defaults to 1.0):
+ Initial value to use for layer scale.
+ use_swiglu_ffn (`bool`, *optional*, defaults to `False`):
+ Whether to use the SwiGLU feedforward neural network.
+ num_register_tokens (`int`, *optional*, defaults to 4):
+ Number of register tokens to use.
+ apply_layernorm (`bool`, *optional*, defaults to `True`):
+ Whether to apply layer normalization to the feature maps in case the model is used as backbone.
+ reshape_hidden_states (`bool`, *optional*, defaults to `True`):
+ Whether to reshape the feature maps to 4D tensors of shape `(batch_size, hidden_size, height, width)` in
+ case the model is used as backbone. If `False`, the feature maps will be 3D tensors of shape `(batch_size,
+ seq_len, hidden_size)`.
+
+ Example:
+
+ ```python
+ >>> from transformers import Dinov2WithRegistersConfig, Dinov2WithRegistersModel
+
+ >>> # Initializing a Dinov2WithRegisters base style configuration
+ >>> configuration = Dinov2WithRegistersConfig()
+
+ >>> # Initializing a model (with random weights) from the base style configuration
+ >>> model = Dinov2WithRegistersModel(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "dinov2_with_registers"
+
+ hidden_size: int = 768
+ num_hidden_layers: int = 12
+ num_attention_heads: int = 12
+ mlp_ratio: int = 4
+ hidden_act: str = "gelu"
+ hidden_dropout_prob: float | int = 0.0
+ attention_probs_dropout_prob: float | int = 0.0
+ initializer_range: float = 0.02
+ layer_norm_eps: float = 1e-6
+ image_size: int | list[int] | tuple[int, int] = 224
+ patch_size: int | list[int] | tuple[int, int] = 16
+ num_channels: int = 3
+ qkv_bias: bool = True
+ layerscale_value: float = 1.0
+ drop_path_rate: float | int = 0.0
+ use_swiglu_ffn: bool = False
+ num_register_tokens: int = 4
+ _out_features: list[str] | None = None
+ _out_indices: list[int] | None = None
+ apply_layernorm: bool = True
+ reshape_hidden_states: bool = True
+
+ def __post_init__(self, **kwargs):
+ self.stage_names = ["stem"] + [f"stage{idx}" for idx in range(1, self.num_hidden_layers + 1)]
+ self.set_output_features_output_indices(
+ out_indices=kwargs.pop("out_indices", None), out_features=kwargs.pop("out_features", None)
+ )
+ super().__post_init__(**kwargs)
+
+
+class Dinov2WithRegistersPatchEmbeddings(Dinov2PatchEmbeddings):
+ pass
+
+
+class Dinov2WithRegistersEmbeddings(nn.Module):
+ """
+ Construct the CLS token, mask token, register tokens, position and patch embeddings.
+ """
+
+ def __init__(self, config: Dinov2WithRegistersConfig) -> None:
+ super().__init__()
+
+ self.cls_token = nn.Parameter(torch.randn(1, 1, config.hidden_size))
+ self.mask_token = nn.Parameter(torch.zeros(1, config.hidden_size))
+ self.register_tokens = nn.Parameter(torch.zeros(1, config.num_register_tokens, config.hidden_size))
+ self.patch_embeddings = Dinov2WithRegistersPatchEmbeddings(config)
+ num_patches = self.patch_embeddings.num_patches
+ self.position_embeddings = nn.Parameter(torch.randn(1, num_patches + 1, config.hidden_size))
+ self.dropout = nn.Dropout(config.hidden_dropout_prob)
+ self.patch_size = config.patch_size
+ self.config = config
+
+ def interpolate_pos_encoding(self, embeddings: torch.Tensor, height: int, width: int) -> torch.Tensor:
+ """
+ This method allows to interpolate the pre-trained position encodings, to be able to use the model on higher
+ resolution images. This implementation supports torch.jit tracing while maintaining backwards compatibility
+ with the original implementation.
+
+ Adapted from:
+ - https://github.com/facebookresearch/dino/blob/main/vision_transformer.py
+ - https://github.com/facebookresearch/dinov2/blob/main/dinov2/models/vision_transformer.py
+ """
+ num_patches = embeddings.shape[1] - 1
+ num_positions = self.position_embeddings.shape[1] - 1
+
+ # Skip interpolation for matching dimensions (unless tracing)
+ if not torch.jit.is_tracing() and num_patches == num_positions and height == width:
+ return self.position_embeddings
+
+ # Handle class token and patch embeddings separately
+ class_pos_embed = self.position_embeddings[:, 0]
+ patch_pos_embed = self.position_embeddings[:, 1:]
+ dim = embeddings.shape[-1]
+
+ # Calculate new dimensions
+ height = height // self.config.patch_size
+ width = width // self.config.patch_size
+
+ # Reshape for interpolation
+ sqrt_num_positions = torch_int(num_positions**0.5)
+ patch_pos_embed = patch_pos_embed.reshape(1, sqrt_num_positions, sqrt_num_positions, dim)
+ patch_pos_embed = patch_pos_embed.permute(0, 3, 1, 2)
+
+ # Store original dtype for restoration after interpolation
+ target_dtype = patch_pos_embed.dtype
+
+ # Interpolate at float32 precision
+ patch_pos_embed = nn.functional.interpolate(
+ patch_pos_embed.to(dtype=torch.float32),
+ size=(torch_int(height), torch_int(width)), # Explicit size instead of scale_factor
+ mode="bicubic",
+ align_corners=False,
+ antialias=True,
+ ).to(dtype=target_dtype)
+
+ # Validate output dimensions if not tracing
+ if not torch.jit.is_tracing():
+ if int(height) != patch_pos_embed.shape[-2] or int(width) != patch_pos_embed.shape[-1]:
+ raise ValueError("Width or height does not match with the interpolated position embeddings")
+
+ # Reshape back to original format
+ patch_pos_embed = patch_pos_embed.permute(0, 2, 3, 1).view(1, -1, dim)
+
+ # Combine class and patch embeddings
+ return torch.cat((class_pos_embed.unsqueeze(0), patch_pos_embed), dim=1)
+
+ def forward(self, pixel_values: torch.Tensor, bool_masked_pos: torch.Tensor | None = None) -> torch.Tensor:
+ batch_size, _, height, width = pixel_values.shape
+ target_dtype = self.patch_embeddings.projection.weight.dtype
+ embeddings = self.patch_embeddings(pixel_values.to(dtype=target_dtype))
+
+ if bool_masked_pos is not None:
+ embeddings = torch.where(
+ bool_masked_pos.unsqueeze(-1), self.mask_token.to(embeddings.dtype).unsqueeze(0), embeddings
+ )
+
+ # add the [CLS] token to the embedded patch tokens
+ cls_tokens = self.cls_token.expand(batch_size, -1, -1)
+ embeddings = torch.cat((cls_tokens, embeddings), dim=1)
+
+ # add positional encoding to each token
+ embeddings = embeddings + self.interpolate_pos_encoding(embeddings, height, width)
+
+ # add register tokens
+ embeddings = torch.cat(
+ (embeddings[:, :1], self.register_tokens.expand(embeddings.shape[0], -1, -1), embeddings[:, 1:]), dim=1
+ )
+
+ embeddings = self.dropout(embeddings)
+
+ return embeddings
+
+
+class Dinov2WithRegistersPreTrainedModel(Dinov2PreTrainedModel):
+ @torch.no_grad()
+ def _init_weights(self, module: nn.Linear | nn.Conv2d | nn.LayerNorm) -> None:
+ """Initialize the weights"""
+ if isinstance(module, (nn.Linear, nn.Conv2d)):
+ init.trunc_normal_(module.weight, mean=0.0, std=self.config.initializer_range)
+ if module.bias is not None:
+ init.zeros_(module.bias)
+ elif isinstance(module, nn.LayerNorm):
+ init.zeros_(module.bias)
+ init.ones_(module.weight)
+ elif isinstance(module, Dinov2WithRegistersEmbeddings):
+ init.trunc_normal_(module.position_embeddings, mean=0.0, std=self.config.initializer_range)
+ init.trunc_normal_(module.cls_token, mean=0.0, std=self.config.initializer_range)
+ init.zeros_(module.mask_token)
+ init.zeros_(module.register_tokens)
+ elif isinstance(module, Dinov2WithRegistersLayerScale): # noqa: F821
+ init.constant_(module.lambda1, self.config.layerscale_value)
+
+
+class Dinov2WithRegistersEncoder(Dinov2Encoder):
+ pass
+
+
+class Dinov2WithRegistersModel(Dinov2Model):
+ pass
+
+
+class Dinov2WithRegistersForImageClassification(Dinov2ForImageClassification):
+ def forward(
+ self,
+ pixel_values: torch.Tensor | None = None,
+ labels: torch.Tensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> ImageClassifierOutput:
+ r"""
+ labels (`torch.LongTensor` of shape `(batch_size,)`, *optional*):
+ Labels for computing the image classification/regression loss. Indices should be in `[0, ...,
+ config.num_labels - 1]`. If `config.num_labels == 1` a regression loss is computed (Mean-Square loss), If
+ `config.num_labels > 1` a classification loss is computed (Cross-Entropy).
+ """
+
+ outputs: BaseModelOutputWithPooling = self.dinov2_with_registers(pixel_values, **kwargs)
+ sequence_output = outputs.last_hidden_state # batch_size, sequence_length, hidden_size
+
+ cls_token = sequence_output[:, 0]
+ # cls and register tokens should not be included in patch tokens variable
+ patch_tokens = sequence_output[:, 1 + self.config.num_register_tokens :]
+
+ linear_input = torch.cat([cls_token, patch_tokens.mean(dim=1)], dim=1)
+ logits = self.classifier(linear_input)
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(labels, logits, self.config, **kwargs)
+
+ return ImageClassifierOutput(
+ loss=loss,
+ logits=logits,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ )
+
+
+class Dinov2WithRegistersBackbone(Dinov2Backbone):
+ def __init__(self, config):
+ super().__init__(config)
+
+ self.num_register_tokens = config.num_register_tokens
+ self.num_features = [config.hidden_size for _ in range(config.num_hidden_layers + 1)]
+ self.embeddings = Dinov2WithRegistersEmbeddings(config)
+ self.encoder = Dinov2WithRegistersEncoder(config)
+
+ self.layernorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ def get_input_embeddings(self) -> Dinov2WithRegistersPatchEmbeddings:
+ return self.embeddings.patch_embeddings
+
+ def forward(
+ self,
+ pixel_values: torch.Tensor,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> BackboneOutput:
+ r"""
+ Examples:
+
+ ```python
+ >>> from transformers import AutoImageProcessor, AutoBackbone
+ >>> import torch
+ >>> from PIL import Image
+ >>> import httpx
+ >>> from io import BytesIO
+
+ >>> url = "http://images.cocodataset.org/val2017/000000039769.jpg"
+ >>> with httpx.stream("GET", url) as response:
+ ... image = Image.open(BytesIO(response.read()))
+
+ >>> processor = AutoImageProcessor.from_pretrained("facebook/dinov2-with-registers-base")
+ >>> model = AutoBackbone.from_pretrained(
+ ... "facebook/dinov2-with-registers-base", out_features=["stage2", "stage5", "stage8", "stage11"]
+ ... )
+
+ >>> inputs = processor(image, return_tensors="pt")
+
+ >>> outputs = model(**inputs)
+ >>> feature_maps = outputs.feature_maps
+ >>> list(feature_maps[-1].shape)
+ [1, 768, 16, 16]
+ ```"""
+ kwargs["output_hidden_states"] = True # required to extract layers for the stages
+
+ embedding_output = self.embeddings(pixel_values)
+ output: BaseModelOutput = self.encoder(embedding_output, **kwargs)
+ hidden_states = output.hidden_states
+
+ feature_maps = []
+ for stage, hidden_state in zip(self.stage_names, hidden_states):
+ if stage in self.out_features:
+ if self.config.apply_layernorm:
+ hidden_state = self.layernorm(hidden_state)
+ if self.config.reshape_hidden_states:
+ hidden_state = hidden_state[:, 1 + self.num_register_tokens :]
+ # this was actually a bug in the original implementation that we copied here,
+ # cause normally the order is height, width
+ batch_size, _, height, width = pixel_values.shape
+ patch_size = self.config.patch_size
+ hidden_state = hidden_state.reshape(batch_size, height // patch_size, width // patch_size, -1)
+ hidden_state = hidden_state.permute(0, 3, 1, 2).contiguous()
+ feature_maps.append(hidden_state)
+
+ return BackboneOutput(
+ feature_maps=tuple(feature_maps),
+ hidden_states=hidden_states,
+ attentions=output.attentions,
+ )
+
+
+__all__ = [
+ "Dinov2WithRegistersConfig",
+ "Dinov2WithRegistersPreTrainedModel",
+ "Dinov2WithRegistersModel",
+ "Dinov2WithRegistersForImageClassification",
+ "Dinov2WithRegistersBackbone",
+]
diff --git a/third_party/transformers/src/transformers/models/doge/__init__.py b/third_party/transformers/src/transformers/models/doge/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..8c214c32dfcdecd28e5afddab2bc266120f45283
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/doge/__init__.py
@@ -0,0 +1,27 @@
+# Copyright 2025 Jingze Shi and the HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_doge import *
+ from .modeling_doge import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/doge/configuration_doge.py b/third_party/transformers/src/transformers/models/doge/configuration_doge.py
new file mode 100644
index 0000000000000000000000000000000000000000..8518d9021458b170fc4773705a425a62eafee752
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/doge/configuration_doge.py
@@ -0,0 +1,110 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/doge/modular_doge.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_doge.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2025 Jingze Shi and the HuggingFace Inc. team. All rights reserved.
+#
+# The Doge family of small language models is trained by SmallDoge Team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...modeling_rope_utils import RopeParameters
+from ...utils import auto_docstring
+
+
+@auto_docstring(checkpoint="SmallDoge/Doge-320M")
+@strict
+class DogeConfig(PreTrainedConfig):
+ r"""
+ keep_window_size (`int`, *optional*, defaults to 2048):
+ The window size of tokens that are not dynamically masked, and dynamic masking is only performed when the sequence length exceeds this value.
+ is_moe (`bool`, *optional*, defaults to `False`):
+ Whether to use the Cross Domain Mixture of Experts, if `True`, the MoE will inherit the MLP to initialize.
+
+ ```python
+ >>> from transformers import DogeConfig, DogeModel
+
+ >>> # Initializing a Doge-320M style configuration
+ >>> configuration = DogeConfig()
+
+ >>> # Initializing a model from the Doge-320M style configuration
+ >>> model = DogeModel(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "doge"
+ keys_to_ignore_at_inference = ["past_key_values"]
+ # Default tensor parallel plan for base model `DogeModel`
+ base_model_tp_plan = {
+ "layers.*.self_attn.q_proj": "colwise",
+ "layers.*.self_attn.k_proj": "colwise",
+ "layers.*.self_attn.v_proj": "colwise",
+ "layers.*.self_attn.dt_proj": "rowwise",
+ "layers.*.self_attn.o_proj": "rowwise",
+ "layers.*.mlp.gate_proj": "colwise",
+ "layers.*.mlp.up_proj": "colwise",
+ "layers.*.mlp.down_proj": "rowwise",
+ "layers.*.mlp.router_gate": "colwise_gather_output",
+ "layers.*.mlp.down_embed": "rowwise_split_input",
+ "layers.*.mlp.up_embed": "rowwise_split_input",
+ }
+ base_model_pp_plan = {
+ "embed_tokens": (["input_ids"], ["inputs_embeds"]),
+ "layers": (["hidden_states", "attention_mask"], ["hidden_states"]),
+ "norm": (["hidden_states"], ["hidden_states"]),
+ }
+
+ vocab_size: int = 32768
+ hidden_size: int = 1024
+ intermediate_size: int = 2048
+ num_hidden_layers: int = 32
+ hidden_dropout: float | int = 0.0
+ hidden_act: str = "silu"
+ initializer_range: float = 0.02
+ rms_norm_eps: float = 1e-06
+ use_cache: bool = True
+ tie_word_embeddings: bool = False
+ max_position_embeddings: int = 2048
+ rope_parameters: RopeParameters | dict | None = None
+ num_attention_heads: int = 8
+ num_key_value_heads: int | None = None
+ attention_bias: bool = False
+ attention_dropout: float | None = 0.0
+ mlp_bias: bool = False
+ sliding_window: int | None = None
+ keep_window_size: int = 2048
+ is_moe: bool = False
+ num_experts: int = 16384
+ num_experts_per_tok: int = 64
+ norm_topk_prob: bool = False
+ output_router_logits: bool = False
+ router_aux_loss_coef: float = 0.001
+ pad_token_id: int | None = None
+ bos_token_id: int | None = None
+ eos_token_id: int | list[int] | None = None
+
+ def __post_init__(self, **kwargs):
+ # for backward compatibility
+ if self.num_key_value_heads is None:
+ self.num_key_value_heads = self.num_attention_heads
+
+ super().__post_init__(**kwargs)
+
+
+__all__ = ["DogeConfig"]
diff --git a/third_party/transformers/src/transformers/models/doge/convert_doge_weights_to_hf.py b/third_party/transformers/src/transformers/models/doge/convert_doge_weights_to_hf.py
new file mode 100644
index 0000000000000000000000000000000000000000..cde4350a15c4bd24bbc42da73bc320380cf25e25
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/doge/convert_doge_weights_to_hf.py
@@ -0,0 +1,126 @@
+import argparse
+import json
+import os
+import re
+
+import torch
+from safetensors.torch import load_file
+
+from transformers import DogeConfig, DogeForCausalLM
+
+
+# fmt: off
+# `None` means we drop the key
+STATE_DICT_MAPPING = {
+ # CausalLM keys
+ r"^lm_head.weight": r"lm_head.weight",
+
+ # Model keys
+ r"^model.word_embed.weight": r"model.embed_tokens.weight",
+ r"^model.rotary_emb.rotary_emb": r"model.rotary_emb.rotary_emb",
+ r"^model.final_layernorm.weight": r"model.norm.weight",
+
+ # Layers keys
+ r"^model.layers.(\d+).pre_layernorm.weight": r"model.layers.\1.input_layernorm.weight",
+ r"^model.layers.(\d+).pre_residual.weight": r"model.layers.\1.input_residual",
+ r"^model.layers.(\d+).post_layernorm.weight": r"model.layers.\1.post_attention_layernorm.weight",
+ r"^model.layers.(\d+).post_residual.weight": r"model.layers.\1.post_attention_residual",
+
+ # Attention keys
+ r"^model.layers.(\d+).self_attn.q_proj.weight": r"model.layers.\1.self_attn.q_proj.weight",
+ r"^model.layers.(\d+).self_attn.k_proj.weight": r"model.layers.\1.self_attn.k_proj.weight",
+ r"^model.layers.(\d+).self_attn.v_proj.weight": r"model.layers.\1.self_attn.v_proj.weight",
+ r"^model.layers.(\d+).self_attn.A": r"model.layers.\1.self_attn.A",
+ r"^model.layers.(\d+).self_attn.dt_proj.weight": r"model.layers.\1.self_attn.dt_proj.weight",
+ r"^model.layers.(\d+).self_attn.o_proj.weight": r"model.layers.\1.self_attn.o_proj.weight",
+
+ # Feedforward keys
+ r"^model.layers.(\d+).feed_forward.gate_proj.weight": r"model.layers.\1.mlp.gate_proj.weight",
+ r"^model.layers.(\d+).feed_forward.up_proj.weight": r"model.layers.\1.mlp.up_proj.weight",
+ r"^model.layers.(\d+).feed_forward.down_proj.weight": r"model.layers.\1.mlp.down_proj.weight",
+ r"^model.layers.(\d+).feed_forward.router_gate.weight": r"model.layers.\1.mlp.router_gate.weight",
+ r"^model.layers.(\d+).feed_forward.router_gate.bias": None,
+ r"^model.layers.(\d+).feed_forward.down_embed.weight": r"model.layers.\1.mlp.down_embed.weight",
+ r"^model.layers.(\d+).feed_forward.up_embed.weight": r"model.layers.\1.mlp.up_embed.weight",
+}
+# fmt: on
+
+
+def load_weights(input_dir: str):
+ safetensor_files = [os.path.join(input_dir, x) for x in os.listdir(input_dir) if x.endswith(".safetensors")]
+
+ all_weights = {}
+
+ if safetensor_files:
+ if len(safetensor_files) == 1:
+ tensors = load_file(safetensor_files[0])
+ all_weights.update(tensors)
+ return all_weights
+ safetensor_files = sorted(safetensor_files, key=lambda x: int(x.rsplit("-", 3)[1]))
+ for file in safetensor_files:
+ tensors = load_file(file)
+ all_weights.update(tensors)
+ return all_weights
+
+ else:
+ raise ValueError("No .safetensors or .bin files found in the specified directory.")
+
+
+def map_old_key_to_new(old_key):
+ for pattern, replacement in STATE_DICT_MAPPING.items():
+ if replacement is None:
+ if re.fullmatch(pattern, old_key):
+ return None
+ else:
+ new_key, n_replace = re.subn(pattern, replacement, old_key)
+ # Early exit of the loop
+ if n_replace > 0:
+ return new_key
+
+ raise ValueError(f"Key: {old_key} could not be mapped (check the mapping).")
+
+
+def convert_state_dict(original_state_dict: dict, config: DogeConfig):
+ new_dict = {}
+
+ for old_key, value in original_state_dict.items():
+ new_key = map_old_key_to_new(old_key)
+ if new_key is None:
+ continue
+ new_dict[new_key] = value
+ return new_dict
+
+
+def convert_doge_model(input_dir, output_dir):
+ # Load and convert config
+ with open(os.path.join(input_dir, "config.json")) as f:
+ config = json.load(f)
+ config = DogeConfig(**config)
+ config.save_pretrained(output_dir)
+
+ # Load and convert weights
+ original_state_dict = load_weights(input_dir)
+ new_dict = convert_state_dict(original_state_dict, config)
+ with torch.device("meta"):
+ model = DogeForCausalLM(config)
+ if config.tie_word_embeddings:
+ new_dict["lm_head.weight"] = new_dict["model.embed_tokens.weight"]
+ model.load_state_dict(new_dict, strict=True, assign=True)
+ model.save_pretrained(output_dir)
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "input_dir",
+ type=str,
+ help="Location of the local folder copied from the Hub.",
+ )
+ parser.add_argument(
+ "output_dir",
+ type=str,
+ help="Location to write HF model.",
+ )
+
+ args = parser.parse_args()
+ convert_doge_model(args.input_dir, args.output_dir)
diff --git a/third_party/transformers/src/transformers/models/doge/modeling_doge.py b/third_party/transformers/src/transformers/models/doge/modeling_doge.py
new file mode 100644
index 0000000000000000000000000000000000000000..4aad59b52a9af9c01e956da26b8ddae2c157e43e
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/doge/modeling_doge.py
@@ -0,0 +1,822 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/doge/modular_doge.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_doge.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2025 Jingze Shi and the HuggingFace Inc. team. All rights reserved.
+#
+# The Doge family of small language models is trained by SmallDoge Team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import math
+from collections.abc import Callable
+from typing import Optional, Union
+
+import torch
+import torch.nn.functional as F
+from torch import nn
+
+from ... import initialization as init
+from ...activations import ACT2FN
+from ...cache_utils import Cache, DynamicCache
+from ...generation import GenerationMixin
+from ...integrations import use_kernel_forward_from_hub, use_kernel_func_from_hub
+from ...integrations.flex_attention import compile_friendly_flex_attention
+from ...masking_utils import create_causal_mask, create_sliding_window_causal_mask
+from ...modeling_layers import GenericForSequenceClassification, GradientCheckpointingLayer
+from ...modeling_outputs import MoeCausalLMOutputWithPast, MoeModelOutputWithPast
+from ...modeling_rope_utils import ROPE_INIT_FUNCTIONS, dynamic_rope_update
+from ...modeling_utils import AttentionInterface, PreTrainedModel
+from ...processing_utils import Unpack
+from ...utils import TransformersKwargs, auto_docstring, can_return_tuple, is_torch_flex_attn_available
+from ...utils.generic import maybe_autocast, merge_with_config_defaults
+from ...utils.output_capturing import OutputRecorder, capture_outputs
+from .configuration_doge import DogeConfig
+
+
+if is_torch_flex_attn_available():
+ from torch.nn.attention.flex_attention import BlockMask
+
+
+@use_kernel_forward_from_hub("RMSNorm")
+class DogeRMSNorm(nn.Module):
+ def __init__(self, hidden_size, eps: float = 1e-6) -> None:
+ """
+ DogeRMSNorm is equivalent to T5LayerNorm
+ """
+ super().__init__()
+ self.weight = nn.Parameter(torch.ones(hidden_size))
+ self.variance_epsilon = eps
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ input_dtype = hidden_states.dtype
+ hidden_states = hidden_states.to(torch.float32)
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
+ hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
+ return self.weight * hidden_states.to(input_dtype)
+
+ def extra_repr(self):
+ return f"{tuple(self.weight.shape)}, eps={self.variance_epsilon}"
+
+
+class DogeRotaryEmbedding(nn.Module):
+ inv_freq: torch.Tensor # fix linting for `register_buffer`
+
+ def __init__(self, config: DogeConfig, device=None):
+ super().__init__()
+ self.max_seq_len_cached = config.max_position_embeddings
+ self.original_max_seq_len = config.max_position_embeddings
+
+ self.config = config
+
+ self.rope_type = self.config.rope_parameters["rope_type"]
+ rope_init_fn: Callable = self.compute_default_rope_parameters
+ if self.rope_type != "default":
+ rope_init_fn = ROPE_INIT_FUNCTIONS[self.rope_type]
+ inv_freq, self.attention_scaling = rope_init_fn(self.config, device)
+
+ self.register_buffer("inv_freq", inv_freq, persistent=False)
+ self.register_buffer("original_inv_freq", inv_freq.clone(), persistent=False)
+
+ @staticmethod
+ def compute_default_rope_parameters(
+ config: DogeConfig | None = None,
+ device: Optional["torch.device"] = None,
+ seq_len: int | None = None,
+ ) -> tuple["torch.Tensor", float]:
+ """
+ Computes the inverse frequencies according to the original RoPE implementation
+ Args:
+ config ([`~transformers.PreTrainedConfig`]):
+ The model configuration.
+ device (`torch.device`):
+ The device to use for initialization of the inverse frequencies.
+ seq_len (`int`, *optional*):
+ The current sequence length. Unused for this type of RoPE.
+ Returns:
+ Tuple of (`torch.Tensor`, `float`), containing the inverse frequencies for the RoPE embeddings and the
+ post-processing scaling factor applied to the computed cos/sin (unused in this type of RoPE).
+ """
+ base = config.rope_parameters["rope_theta"]
+ dim = getattr(config, "head_dim", None) or config.hidden_size // config.num_attention_heads
+
+ attention_factor = 1.0 # Unused in this type of RoPE
+
+ # Compute the inverse frequencies
+ inv_freq = 1.0 / (
+ base ** (torch.arange(0, dim, 2, dtype=torch.int64).to(device=device, dtype=torch.float) / dim)
+ )
+ return inv_freq, attention_factor
+
+ @torch.no_grad()
+ @dynamic_rope_update # power user: used with advanced RoPE types (e.g. dynamic rope)
+ def forward(self, x, position_ids):
+ inv_freq_expanded = self.inv_freq[None, :, None].float().expand(position_ids.shape[0], -1, 1).to(x.device)
+ position_ids_expanded = position_ids[:, None, :].float()
+
+ device_type = x.device.type if isinstance(x.device.type, str) and x.device.type != "mps" else "cpu"
+ with maybe_autocast(device_type=device_type, enabled=False): # Force float32
+ freqs = (inv_freq_expanded.float() @ position_ids_expanded.float()).transpose(1, 2)
+ emb = torch.cat((freqs, freqs), dim=-1)
+ cos = emb.cos() * self.attention_scaling
+ sin = emb.sin() * self.attention_scaling
+
+ return cos.to(dtype=x.dtype), sin.to(dtype=x.dtype)
+
+
+def rotate_half(x):
+ """Rotates half the hidden dims of the input."""
+ x1 = x[..., : x.shape[-1] // 2]
+ x2 = x[..., x.shape[-1] // 2 :]
+ return torch.cat((-x2, x1), dim=-1)
+
+
+@use_kernel_func_from_hub("rotary_pos_emb")
+def apply_rotary_pos_emb(q, k, cos, sin, unsqueeze_dim=1):
+ """Applies Rotary Position Embedding to the query and key tensors.
+
+ Args:
+ q (`torch.Tensor`): The query tensor.
+ k (`torch.Tensor`): The key tensor.
+ cos (`torch.Tensor`): The cosine part of the rotary embedding.
+ sin (`torch.Tensor`): The sine part of the rotary embedding.
+ unsqueeze_dim (`int`, *optional*, defaults to 1):
+ The 'unsqueeze_dim' argument specifies the dimension along which to unsqueeze cos[position_ids] and
+ sin[position_ids] so that they can be properly broadcasted to the dimensions of q and k. For example, note
+ that cos[position_ids] and sin[position_ids] have the shape [batch_size, seq_len, head_dim]. Then, if q and
+ k have the shape [batch_size, heads, seq_len, head_dim], then setting unsqueeze_dim=1 makes
+ cos[position_ids] and sin[position_ids] broadcastable to the shapes of q and k. Similarly, if q and k have
+ the shape [batch_size, seq_len, heads, head_dim], then set unsqueeze_dim=2.
+ Returns:
+ `tuple(torch.Tensor)` comprising of the query and key tensors rotated using the Rotary Position Embedding.
+ """
+ cos = cos.unsqueeze(unsqueeze_dim)
+ sin = sin.unsqueeze(unsqueeze_dim)
+ q_embed = (q * cos) + (rotate_half(q) * sin)
+ k_embed = (k * cos) + (rotate_half(k) * sin)
+ return q_embed, k_embed
+
+
+def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
+ """
+ This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
+ num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
+ """
+ batch, num_key_value_heads, slen, head_dim = hidden_states.shape
+ if n_rep == 1:
+ return hidden_states
+ hidden_states = hidden_states[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim)
+ return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
+
+
+def eager_attention_forward(
+ module: nn.Module,
+ query: torch.Tensor,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ attention_mask: torch.Tensor | None,
+ scaling: float,
+ dropout: float = 0.0,
+ **kwargs: Unpack[TransformersKwargs],
+):
+ key_states = repeat_kv(key, module.num_key_value_groups)
+ value_states = repeat_kv(value, module.num_key_value_groups)
+
+ attn_weights = torch.matmul(query, key_states.transpose(2, 3)) * scaling
+ if attention_mask is not None:
+ attn_weights = attn_weights + attention_mask
+
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query.dtype)
+ attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
+ attn_output = torch.matmul(attn_weights, value_states)
+ attn_output = attn_output.transpose(1, 2).contiguous()
+
+ return attn_output, attn_weights
+
+
+def flex_attention_forward(
+ module: nn.Module,
+ query: torch.Tensor,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ attention_mask: Union[torch.Tensor, "BlockMask"],
+ scaling: float | None = None,
+ softcap: float | None = None,
+ **kwargs,
+) -> tuple[torch.Tensor, torch.Tensor]:
+ block_mask = None
+ causal_mask = None
+ if isinstance(attention_mask, BlockMask):
+ block_mask = attention_mask
+ else:
+ causal_mask = attention_mask
+
+ if causal_mask is not None:
+ causal_mask = causal_mask[:, :, :, : key.shape[-2]]
+
+ def score_mod(score, batch_idx, head_idx, q_idx, kv_idx):
+ if softcap is not None:
+ score = softcap * torch.tanh(score / softcap)
+ if causal_mask is not None:
+ score = score + causal_mask[batch_idx][head_idx][q_idx][kv_idx]
+ return score
+
+ attn_output, attention_weights = compile_friendly_flex_attention(
+ query,
+ key,
+ value,
+ score_mod=score_mod,
+ block_mask=block_mask,
+ enable_gqa=True,
+ scale=scaling,
+ # Last time checked on PyTorch == 2.5.1: Flex Attention always computes the lse regardless.
+ # For simplification, we thus always return it as no additional computations are introduced.
+ return_lse=True,
+ )
+ # lse is returned in float32
+ attention_weights = attention_weights.to(value.dtype)
+ attn_output = attn_output.transpose(1, 2).contiguous()
+
+ return attn_output, attention_weights
+
+
+ALL_ATTENTION_FUNCTIONS = AttentionInterface()
+ALL_ATTENTION_FUNCTIONS["doge_flex_attention"] = flex_attention_forward
+
+
+class DogeAttention(nn.Module):
+ def __init__(self, config: DogeConfig, layer_idx: int | None = None):
+ super().__init__()
+ self.config = config
+ self.layer_idx = layer_idx
+ self.head_dim = getattr(config, "head_dim", config.hidden_size // config.num_attention_heads)
+ self.num_key_value_groups = config.num_attention_heads // config.num_key_value_heads
+ self.scaling = self.head_dim**-0.5
+ self.attention_dropout = config.attention_dropout
+ self.keep_window_size = config.keep_window_size
+
+ self.q_proj = nn.Linear(
+ config.hidden_size, config.num_attention_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.k_proj = nn.Linear(
+ config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.v_proj = nn.Linear(
+ config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias
+ )
+ # dynamic mask for the QK^T attention weights matrix
+ self.A = nn.Parameter(torch.zeros(config.num_key_value_heads))
+ self.dt_proj = nn.Linear(
+ config.num_key_value_heads * self.head_dim, config.num_key_value_heads, bias=config.attention_bias
+ )
+ self.o_proj = nn.Linear(
+ config.num_attention_heads * self.head_dim, config.hidden_size, bias=config.attention_bias
+ )
+ self.q_norm = DogeRMSNorm(self.head_dim, eps=config.rms_norm_eps)
+ self.k_norm = DogeRMSNorm(self.head_dim, eps=config.rms_norm_eps)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor],
+ attention_mask: torch.Tensor | None = None,
+ past_key_values: Cache | None = None,
+ **kwargs,
+ ) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
+ input_shape = hidden_states.shape[:-1]
+ hidden_shape = (*input_shape, -1, self.head_dim)
+
+ query_states = self.q_norm(self.q_proj(hidden_states).view(hidden_shape)).transpose(1, 2)
+ key_states = self.k_norm(self.k_proj(hidden_states).view(hidden_shape)).transpose(1, 2)
+ value_states = self.v_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+
+ cos, sin = position_embeddings
+ query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin)
+
+ if past_key_values is not None:
+ key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx)
+
+ # calculate dynamic mask from value_states
+ dt_states = self.dt_proj(
+ value_states.transpose(1, 2).reshape(value_states.shape[0], value_states.shape[-2], -1)
+ )
+ dt_states = torch.exp(self.A * F.softplus(dt_states)).transpose(-1, -2)
+ attn_mask = self.prepare_dynamic_mask(
+ hidden_states=hidden_states,
+ dt_states=dt_states,
+ keep_window_size=self.keep_window_size,
+ attention_mask=attention_mask,
+ )
+ attn_mask = repeat_kv(attn_mask, self.num_key_value_groups)
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ query_states,
+ key_states,
+ value_states,
+ attention_mask=attn_mask,
+ dropout=0.0 if not self.training else self.attention_dropout,
+ scaling=self.scaling,
+ **kwargs,
+ )
+
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
+ attn_output = self.o_proj(attn_output)
+ return attn_output, attn_weights
+
+ def prepare_dynamic_mask(
+ self,
+ hidden_states: torch.Tensor,
+ dt_states: torch.Tensor,
+ keep_window_size: int = 2048,
+ attention_mask: torch.Tensor | None = None,
+ ):
+ """
+ The core idea of DMA is to calculate the dynamic attention mask to mask the tokens that should be masked, so as to form sparse attention.
+
+ Combine `dt_states` with `attention_mask` to generate the final `attn_mask`.
+
+ Args:
+ hidden_states (`torch.Tensor`): The input hidden_states, used to determine the minimum value of the current input precision.
+ dt_states (`torch.Tensor`): dt_states of shape `(batch_size, num_heads, key_sequence_length)`.
+ keep_window_size (`int`): The window size of tokens that are not dynamically masked, and dynamic masking is only performed when the sequence length exceeds this value.
+ attention_mask (`torch.Tensor`, *optional*): attention mask of shape `(batch_size, 1, query_sequence_length, key_sequence_length)`.
+ """
+ min_dtype = torch.finfo(hidden_states.dtype).min
+ dtype = hidden_states.dtype
+ attn_mask = dt_states[:, :, None, :].expand(
+ -1, -1, hidden_states.shape[1], -1
+ ) # [batch_size, num_heads, query_len, key_len]
+ if attention_mask is not None and not isinstance(attention_mask, BlockMask):
+ if attention_mask.dtype == torch.bool:
+ dtype = hidden_states.dtype
+ attention_mask = torch.where(
+ attention_mask, torch.tensor(0.0, device=attention_mask.device, dtype=dtype), min_dtype
+ )
+ attn_mask = attn_mask.masked_fill(attention_mask[:, :, :, : attn_mask.shape[-1]] != 0, min_dtype)
+ if attn_mask.shape[-1] > keep_window_size:
+ active_mask = torch.zeros_like(attn_mask, dtype=dtype, device=attn_mask.device)
+ topk_indices = torch.topk(attn_mask, keep_window_size, dim=-1, largest=True, sorted=False).indices
+ active_mask = active_mask.scatter(-1, topk_indices, 1.0)
+ attn_mask = attn_mask.masked_fill(active_mask == 0.0, min_dtype)
+ return attn_mask
+
+
+class DogeMLP(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+ self.config = config
+ self.hidden_size = config.hidden_size
+ self.intermediate_size = config.intermediate_size
+ self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=config.mlp_bias)
+ self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=config.mlp_bias)
+ self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=config.mlp_bias)
+ self.act_fn = ACT2FN[config.hidden_act]
+
+ def forward(self, x):
+ down_proj = self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
+ return down_proj
+
+
+class DogeCDMoE(nn.Module):
+ def __init__(self, config: DogeConfig):
+ super().__init__()
+ self.hidden_size = config.hidden_size
+ self.intermediate_size = config.intermediate_size
+ self.act_fn = ACT2FN[config.hidden_act]
+
+ self.num_experts = config.num_experts
+ self.num_keys = math.floor(math.sqrt(self.num_experts))
+ self.top_k = config.num_experts_per_tok
+ self.norm_topk_prob = config.norm_topk_prob
+
+ # shared expert
+ self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=config.mlp_bias)
+ self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=config.mlp_bias)
+ self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=config.mlp_bias)
+
+ # router gate for retrieval experts
+ self.router_gate = nn.Linear(self.hidden_size, self.num_keys * 2, bias=False)
+
+ # routed experts
+ self.down_embed = nn.Embedding(self.num_experts, self.hidden_size)
+ self.up_embed = nn.Embedding(self.num_experts, self.hidden_size)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ **kwargs,
+ ) -> torch.Tensor:
+ bsz, seq_len, _ = hidden_states.shape
+
+ # get routing logits with router gate
+ router_logits = self.router_gate(hidden_states).view(2, bsz * seq_len, -1)
+
+ # get experts with the highest routing logits
+ (scores_x, scores_y), (indices_x, indices_y) = router_logits.topk(self.num_keys, dim=-1)
+ all_scores = scores_x.unsqueeze(-1) + scores_y.unsqueeze(-2)
+ all_indices = indices_x.unsqueeze(-1) * self.num_keys + indices_y.unsqueeze(-2)
+ all_scores = all_scores.view(*all_scores.shape[:-2], -1)
+ all_indices = all_indices.view(*all_indices.shape[:-2], -1)
+ scores, position_indices = all_scores.topk(self.top_k, dim=-1)
+ indices = all_indices.gather(-1, position_indices)
+ routing_weights = F.softmax(scores, dim=-1)
+ if self.norm_topk_prob:
+ routing_weights /= routing_weights.sum(dim=-1, keepdim=True)
+
+ # mix routed experts states with shared expert states
+ down_embed = self.down_embed(indices)
+ up_embed = self.up_embed(indices)
+ experts_weights = torch.matmul(down_embed, hidden_states.view(bsz * seq_len, -1, 1)).view(bsz * seq_len, -1)
+ experts_weights = self.act_fn(experts_weights) * routing_weights
+ experts_states = torch.matmul(experts_weights.view(bsz * seq_len, 1, -1), up_embed).view(bsz, seq_len, -1)
+ hidden_states = self.down_proj(self.act_fn(self.gate_proj(hidden_states)) * self.up_proj(hidden_states))
+ hidden_states = hidden_states + experts_states
+ return hidden_states, router_logits
+
+
+class DogeDecoderLayer(GradientCheckpointingLayer):
+ def __init__(self, config: DogeConfig, layer_idx: int | None = None):
+ super().__init__()
+ self.hidden_dropout = config.hidden_dropout
+
+ self.input_layernorm = DogeRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.self_attn = DogeAttention(config=config, layer_idx=layer_idx)
+ self.input_residual = nn.Parameter(torch.ones(config.hidden_size))
+
+ self.post_attention_layernorm = DogeRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.mlp = DogeMLP(config) if not config.is_moe else DogeCDMoE(config)
+ self.post_attention_residual = nn.Parameter(torch.ones(config.hidden_size))
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ use_cache: bool | None = False,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple[torch.FloatTensor, tuple[torch.FloatTensor, torch.FloatTensor] | None]:
+ # sequence transformation
+ residual = hidden_states
+ hidden_states = self.input_layernorm(hidden_states)
+ hidden_states, self_attn_weights = self.self_attn(
+ hidden_states=hidden_states,
+ position_embeddings=position_embeddings,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ **kwargs,
+ )
+ hidden_states = F.dropout(hidden_states, p=self.hidden_dropout, training=self.training)
+ hidden_states = self.input_residual * residual + hidden_states
+
+ # state transformation
+ residual = hidden_states
+ hidden_states = self.post_attention_layernorm(hidden_states)
+ hidden_states = self.mlp(hidden_states)
+ hidden_states = F.dropout(hidden_states, p=self.hidden_dropout, training=self.training)
+ hidden_states = self.post_attention_residual * residual + hidden_states
+
+ return hidden_states
+
+
+@auto_docstring
+class DogePreTrainedModel(PreTrainedModel):
+ config: DogeConfig
+ base_model_prefix = "model"
+ supports_gradient_checkpointing = True
+ _no_split_modules = ["DogeDecoderLayer"]
+ _skip_keys_device_placement = ["past_key_values"]
+ _supports_flash_attn = False
+ _supports_sdpa = True
+ _supports_flex_attn = True
+ _can_compile_fullgraph = False
+ _supports_attention_backend = True
+ _can_record_outputs = {
+ "router_logits": OutputRecorder(DogeCDMoE, index=1),
+ "hidden_states": DogeDecoderLayer,
+ "attentions": DogeAttention,
+ }
+
+ @torch.no_grad()
+ def _init_weights(self, module):
+ """Initialize the weights"""
+ super()._init_weights(module)
+ if isinstance(module, DogeAttention):
+ if hasattr(module, "A"):
+ init.zeros_(module.A)
+ elif isinstance(module, DogeDecoderLayer):
+ if hasattr(module, "input_residual"):
+ init.ones_(module.input_residual)
+ if hasattr(module, "post_attention_residual"):
+ init.ones_(module.post_attention_residual)
+
+
+@auto_docstring
+class DogeModel(DogePreTrainedModel):
+ def __init__(self, config: DogeConfig):
+ super().__init__(config)
+ self.padding_idx = config.pad_token_id
+ self.vocab_size = config.vocab_size
+
+ self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)
+ self.layers = nn.ModuleList(
+ [DogeDecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
+ )
+ self.norm = DogeRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.rotary_emb = DogeRotaryEmbedding(config=config)
+ self.gradient_checkpointing = False
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @merge_with_config_defaults
+ @capture_outputs
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ use_cache: bool | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> MoeModelOutputWithPast:
+ if (input_ids is None) ^ (inputs_embeds is not None):
+ raise ValueError("You must specify exactly one of input_ids or inputs_embeds")
+
+ if use_cache and past_key_values is None:
+ past_key_values = DynamicCache(config=self.config)
+
+ if inputs_embeds is None:
+ inputs_embeds = self.embed_tokens(input_ids)
+
+ if position_ids is None:
+ past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
+ position_ids = torch.arange(inputs_embeds.shape[1], device=inputs_embeds.device) + past_seen_tokens
+ position_ids = position_ids.unsqueeze(0)
+
+ mask_function = create_causal_mask if self.config.sliding_window is None else create_sliding_window_causal_mask
+ causal_mask = mask_function(
+ config=self.config,
+ inputs_embeds=inputs_embeds,
+ attention_mask=attention_mask,
+ past_key_values=past_key_values,
+ position_ids=position_ids,
+ )
+
+ hidden_states = inputs_embeds
+ position_embeddings = self.rotary_emb(hidden_states, position_ids=position_ids)
+
+ for decoder_layer in self.layers[: self.config.num_hidden_layers]:
+ hidden_states = decoder_layer(
+ hidden_states,
+ attention_mask=causal_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ position_embeddings=position_embeddings,
+ **kwargs,
+ )
+
+ hidden_states = self.norm(hidden_states)
+
+ return MoeModelOutputWithPast( # only diff with Mistral is the output type, we need MoE
+ last_hidden_state=hidden_states,
+ past_key_values=past_key_values,
+ )
+
+
+def load_balancing_loss_func(
+ gate_logits: torch.Tensor | tuple[torch.Tensor] | None,
+ num_experts: int | None = None,
+ num_keys: int | None = None,
+ top_k: int = 2,
+ attention_mask: torch.Tensor | None = None,
+) -> torch.Tensor | int:
+ r"""
+ Computes auxiliary load balancing loss as in Switch Transformer - implemented in Pytorch.
+
+ See Switch Transformer (https://huggingface.co/papers/2101.03961) for more details. This function implements the loss
+ function presented in equations (4) - (6) of the paper. It aims at penalizing cases where the routing between
+ experts is too unbalanced.
+
+ Args:
+ gate_logits:
+ Logits from the `router_gate`, should be a tuple of model.config.num_hidden_layers tensors of
+ shape [2, batch_size * sequence_length, num_keys].
+ num_experts:
+ Number of experts
+ num_keys:
+ Number of keys
+ top_k:
+ The number of experts to route per-token, can be also interpreted as the `top-k` routing
+ parameter.
+ attention_mask (`torch.Tensor`, *optional*):
+ The attention_mask used in forward function
+ shape [batch_size X sequence_length] if not None.
+
+ Returns:
+ The auxiliary loss.
+ """
+ if gate_logits is None or not isinstance(gate_logits, tuple):
+ return 0
+
+ compute_dtype = gate_logits[0].dtype
+ compute_device = gate_logits[0].device
+ all_expert_indices = []
+ all_routing_weights = []
+
+ for layer_gate_logits in gate_logits:
+ layer_gate_logits = layer_gate_logits.to(compute_device)
+
+ (scores_x, scores_y), (indices_x, indices_y) = layer_gate_logits.topk(num_keys, dim=-1)
+
+ all_scores = scores_x.unsqueeze(-1) + scores_y.unsqueeze(-2)
+ all_indices = indices_x.unsqueeze(-1) * num_keys + indices_y.unsqueeze(-2)
+ all_scores = all_scores.view(*all_scores.shape[:-2], -1)
+ all_indices = all_indices.view(*all_indices.shape[:-2], -1)
+
+ _, position_indices = all_scores.topk(top_k, dim=-1)
+ expert_indices = all_indices.gather(-1, position_indices)
+
+ routing_weights = F.softmax(all_scores, dim=-1)
+
+ all_expert_indices.append(expert_indices)
+ all_routing_weights.append(routing_weights)
+ all_expert_indices = torch.cat(all_expert_indices, dim=0)
+ all_routing_weights = torch.cat(all_routing_weights, dim=0)
+
+ if attention_mask is None:
+ # Compute the percentage of tokens routed to each experts
+ all_expert_indices = all_expert_indices.view(-1)
+ tokens_per_expert = torch.zeros(num_experts, dtype=compute_dtype, device=compute_device)
+ pad = torch.ones_like(all_expert_indices, dtype=compute_dtype, device=compute_device)
+ tokens_per_expert = tokens_per_expert.scatter_add_(0, all_expert_indices, pad) / all_expert_indices.shape[0]
+
+ # Compute the average probability of routing to these experts
+ router_prob_per_expert = torch.mean(all_routing_weights, dim=0)
+ else:
+ batch_size, sequence_length = attention_mask.shape
+ num_hidden_layers = len(gate_logits)
+
+ # Compute the mask that masks all padding tokens as 0 with the same shape of expert_mask
+ expert_attention_mask = (
+ attention_mask[None, :, :, None]
+ .expand((num_hidden_layers, batch_size, sequence_length, top_k))
+ .reshape(-1)
+ .to(compute_device)
+ )
+ all_expert_indices = all_expert_indices.view(-1)[expert_attention_mask.bool()]
+
+ # Compute the percentage of tokens routed to each experts
+ tokens_per_expert = torch.zeros(num_experts, dtype=compute_dtype, device=compute_device)
+ pad = torch.ones_like(all_expert_indices, dtype=compute_dtype, device=compute_device)
+ tokens_per_expert = tokens_per_expert.scatter_add_(0, all_expert_indices, pad) / torch.sum(
+ expert_attention_mask
+ )
+
+ # Compute the mask that masks all padding tokens as 0 with the same shape of tokens_per_expert
+ router_per_expert_attention_mask = (
+ attention_mask[None, :, :, None]
+ .expand((num_hidden_layers, batch_size, sequence_length, num_experts))
+ .reshape(-1, num_experts)
+ .to(compute_device)
+ )
+
+ # Compute the average probability of routing to these experts
+ router_prob_per_expert = torch.sum(all_routing_weights * router_per_expert_attention_mask, dim=0) / torch.sum(
+ router_per_expert_attention_mask, dim=0
+ )
+
+ overall_loss = torch.sum(tokens_per_expert * router_prob_per_expert)
+ return overall_loss * num_experts
+
+
+@auto_docstring
+class DogeForCausalLM(DogePreTrainedModel, GenerationMixin):
+ _tied_weights_keys = {"lm_head.weight": "model.embed_tokens.weight"}
+ _tp_plan = {"lm_head": "colwise_gather_output"}
+ _pp_plan = {"lm_head": (["hidden_states"], ["logits"])}
+
+ def __init__(self, config):
+ super().__init__(config)
+ self.model = DogeModel(config)
+ self.vocab_size = config.vocab_size
+ self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
+ self.router_aux_loss_coef = config.router_aux_loss_coef
+ self.num_experts = config.num_experts
+ self.num_experts_per_tok = config.num_experts_per_tok
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ output_router_logits: bool | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> MoeCausalLMOutputWithPast:
+ r"""
+ 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]`.
+
+ Example:
+
+ ```python
+ >>> from transformers import AutoTokenizer, DogeForCausalLM
+
+ >>> model = DogeForCausalLM.from_pretrained("SmallDoge/Doge-320M")
+ >>> tokenizer = AutoTokenizer.from_pretrained("SmallDoge/Doge-320M")
+
+ >>> prompt = "Hey, are you conscious? Can you talk to me?"
+ >>> inputs = tokenizer(prompt, return_tensors="pt")
+
+ >>> # Generate
+ >>> generate_ids = model.generate(inputs.input_ids, max_length=30)
+ >>> tokenizer.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]
+ "Hey, are you conscious? Can you talk to me?\nI'm not conscious, but I can talk to you."
+ ```"""
+ output_router_logits = (
+ output_router_logits if output_router_logits is not None else self.config.output_router_logits
+ )
+
+ # decoder outputs consists of (dec_features, layer_state, dec_hidden, dec_attn)
+ outputs: MoeModelOutputWithPast = self.model(
+ input_ids=input_ids,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ hidden_states = outputs.last_hidden_state
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(logits, labels, self.vocab_size, **kwargs)
+
+ aux_loss = None
+ if output_router_logits:
+ aux_loss = load_balancing_loss_func(
+ outputs.router_logits,
+ self.num_experts,
+ math.floor(math.sqrt(self.num_experts)),
+ self.num_experts_per_tok,
+ attention_mask,
+ )
+ if labels is not None:
+ loss += self.router_aux_loss_coef * aux_loss.to(loss.device) # make sure to reside in the same device
+
+ return MoeCausalLMOutputWithPast(
+ loss=loss,
+ aux_loss=aux_loss,
+ logits=logits,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ router_logits=outputs.router_logits,
+ )
+
+
+class DogeForSequenceClassification(GenericForSequenceClassification, DogePreTrainedModel):
+ pass
+
+
+__all__ = ["DogeForCausalLM", "DogeModel", "DogePreTrainedModel", "DogeForSequenceClassification"]
diff --git a/third_party/transformers/src/transformers/models/doge/modular_doge.py b/third_party/transformers/src/transformers/models/doge/modular_doge.py
new file mode 100644
index 0000000000000000000000000000000000000000..8b78126c0a001f23b6e252eaa76356f89c5f7009
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/doge/modular_doge.py
@@ -0,0 +1,659 @@
+# Copyright 2025 Jingze Shi and the HuggingFace Inc. team. All rights reserved.
+#
+# The Doge family of small language models is trained by SmallDoge Team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""PyTorch Doge model."""
+
+import math
+from collections.abc import Callable
+from typing import Union
+
+import torch
+import torch.nn.functional as F
+from huggingface_hub.dataclasses import strict
+from torch import nn
+
+from ... import initialization as init
+from ...activations import ACT2FN
+from ...cache_utils import Cache
+from ...configuration_utils import PreTrainedConfig
+from ...integrations.flex_attention import compile_friendly_flex_attention
+from ...modeling_layers import GradientCheckpointingLayer
+from ...modeling_outputs import MoeCausalLMOutputWithPast, MoeModelOutputWithPast
+from ...modeling_rope_utils import RopeParameters
+from ...modeling_utils import AttentionInterface, PreTrainedModel
+from ...processing_utils import Unpack
+from ...utils import TransformersKwargs, auto_docstring, is_torch_flex_attn_available, logging
+from ...utils.output_capturing import OutputRecorder
+from ..llama.modeling_llama import (
+ LlamaForSequenceClassification,
+ LlamaMLP,
+ LlamaPreTrainedModel,
+ LlamaRMSNorm,
+ LlamaRotaryEmbedding,
+ apply_rotary_pos_emb,
+ eager_attention_forward,
+ repeat_kv,
+)
+from ..mixtral.modeling_mixtral import MixtralForCausalLM, MixtralModel
+
+
+logger = logging.get_logger(__name__)
+
+if is_torch_flex_attn_available():
+ from torch.nn.attention.flex_attention import BlockMask
+
+
+@auto_docstring(checkpoint="SmallDoge/Doge-320M")
+@strict
+class DogeConfig(PreTrainedConfig):
+ r"""
+ keep_window_size (`int`, *optional*, defaults to 2048):
+ The window size of tokens that are not dynamically masked, and dynamic masking is only performed when the sequence length exceeds this value.
+ is_moe (`bool`, *optional*, defaults to `False`):
+ Whether to use the Cross Domain Mixture of Experts, if `True`, the MoE will inherit the MLP to initialize.
+
+ ```python
+ >>> from transformers import DogeConfig, DogeModel
+
+ >>> # Initializing a Doge-320M style configuration
+ >>> configuration = DogeConfig()
+
+ >>> # Initializing a model from the Doge-320M style configuration
+ >>> model = DogeModel(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "doge"
+ keys_to_ignore_at_inference = ["past_key_values"]
+ # Default tensor parallel plan for base model `DogeModel`
+ base_model_tp_plan = {
+ "layers.*.self_attn.q_proj": "colwise",
+ "layers.*.self_attn.k_proj": "colwise",
+ "layers.*.self_attn.v_proj": "colwise",
+ "layers.*.self_attn.dt_proj": "rowwise",
+ "layers.*.self_attn.o_proj": "rowwise",
+ "layers.*.mlp.gate_proj": "colwise",
+ "layers.*.mlp.up_proj": "colwise",
+ "layers.*.mlp.down_proj": "rowwise",
+ "layers.*.mlp.router_gate": "colwise_gather_output",
+ "layers.*.mlp.down_embed": "rowwise_split_input",
+ "layers.*.mlp.up_embed": "rowwise_split_input",
+ }
+ base_model_pp_plan = {
+ "embed_tokens": (["input_ids"], ["inputs_embeds"]),
+ "layers": (["hidden_states", "attention_mask"], ["hidden_states"]),
+ "norm": (["hidden_states"], ["hidden_states"]),
+ }
+
+ vocab_size: int = 32768
+ hidden_size: int = 1024
+ intermediate_size: int = 2048
+ num_hidden_layers: int = 32
+ hidden_dropout: float | int = 0.0
+ hidden_act: str = "silu"
+ initializer_range: float = 0.02
+ rms_norm_eps: float = 1e-06
+ use_cache: bool = True
+ tie_word_embeddings: bool = False
+ max_position_embeddings: int = 2048
+ rope_parameters: RopeParameters | dict | None = None
+ num_attention_heads: int = 8
+ num_key_value_heads: int | None = None
+ attention_bias: bool = False
+ attention_dropout: float | None = 0.0
+ mlp_bias: bool = False
+ sliding_window: int | None = None
+ keep_window_size: int = 2048
+ is_moe: bool = False
+ num_experts: int = 16384
+ num_experts_per_tok: int = 64
+ norm_topk_prob: bool = False
+ output_router_logits: bool = False
+ router_aux_loss_coef: float = 0.001
+ pad_token_id: int | None = None
+ bos_token_id: int | None = None
+ eos_token_id: int | list[int] | None = None
+
+ def __post_init__(self, **kwargs):
+ # for backward compatibility
+ if self.num_key_value_heads is None:
+ self.num_key_value_heads = self.num_attention_heads
+
+ super().__post_init__(**kwargs)
+
+
+class DogeRMSNorm(LlamaRMSNorm):
+ pass
+
+
+class DogeRotaryEmbedding(LlamaRotaryEmbedding):
+ pass
+
+
+def flex_attention_forward(
+ module: nn.Module,
+ query: torch.Tensor,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ attention_mask: Union[torch.Tensor, "BlockMask"],
+ scaling: float | None = None,
+ softcap: float | None = None,
+ **kwargs,
+) -> tuple[torch.Tensor, torch.Tensor]:
+ block_mask = None
+ causal_mask = None
+ if isinstance(attention_mask, BlockMask):
+ block_mask = attention_mask
+ else:
+ causal_mask = attention_mask
+
+ if causal_mask is not None:
+ causal_mask = causal_mask[:, :, :, : key.shape[-2]]
+
+ def score_mod(score, batch_idx, head_idx, q_idx, kv_idx):
+ if softcap is not None:
+ score = softcap * torch.tanh(score / softcap)
+ if causal_mask is not None:
+ score = score + causal_mask[batch_idx][head_idx][q_idx][kv_idx]
+ return score
+
+ attn_output, attention_weights = compile_friendly_flex_attention(
+ query,
+ key,
+ value,
+ score_mod=score_mod,
+ block_mask=block_mask,
+ enable_gqa=True,
+ scale=scaling,
+ # Last time checked on PyTorch == 2.5.1: Flex Attention always computes the lse regardless.
+ # For simplification, we thus always return it as no additional computations are introduced.
+ return_lse=True,
+ )
+ # lse is returned in float32
+ attention_weights = attention_weights.to(value.dtype)
+ attn_output = attn_output.transpose(1, 2).contiguous()
+
+ return attn_output, attention_weights
+
+
+ALL_ATTENTION_FUNCTIONS = AttentionInterface()
+ALL_ATTENTION_FUNCTIONS["doge_flex_attention"] = flex_attention_forward
+
+
+class DogeAttention(nn.Module):
+ def __init__(self, config: DogeConfig, layer_idx: int | None = None):
+ super().__init__()
+ self.config = config
+ self.layer_idx = layer_idx
+ self.head_dim = getattr(config, "head_dim", config.hidden_size // config.num_attention_heads)
+ self.num_key_value_groups = config.num_attention_heads // config.num_key_value_heads
+ self.scaling = self.head_dim**-0.5
+ self.attention_dropout = config.attention_dropout
+ self.keep_window_size = config.keep_window_size
+
+ self.q_proj = nn.Linear(
+ config.hidden_size, config.num_attention_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.k_proj = nn.Linear(
+ config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.v_proj = nn.Linear(
+ config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias
+ )
+ # dynamic mask for the QK^T attention weights matrix
+ self.A = nn.Parameter(torch.zeros(config.num_key_value_heads))
+ self.dt_proj = nn.Linear(
+ config.num_key_value_heads * self.head_dim, config.num_key_value_heads, bias=config.attention_bias
+ )
+ self.o_proj = nn.Linear(
+ config.num_attention_heads * self.head_dim, config.hidden_size, bias=config.attention_bias
+ )
+ self.q_norm = DogeRMSNorm(self.head_dim, eps=config.rms_norm_eps)
+ self.k_norm = DogeRMSNorm(self.head_dim, eps=config.rms_norm_eps)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor],
+ attention_mask: torch.Tensor | None = None,
+ past_key_values: Cache | None = None,
+ **kwargs,
+ ) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
+ input_shape = hidden_states.shape[:-1]
+ hidden_shape = (*input_shape, -1, self.head_dim)
+
+ query_states = self.q_norm(self.q_proj(hidden_states).view(hidden_shape)).transpose(1, 2)
+ key_states = self.k_norm(self.k_proj(hidden_states).view(hidden_shape)).transpose(1, 2)
+ value_states = self.v_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+
+ cos, sin = position_embeddings
+ query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin)
+
+ if past_key_values is not None:
+ key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx)
+
+ # calculate dynamic mask from value_states
+ dt_states = self.dt_proj(
+ value_states.transpose(1, 2).reshape(value_states.shape[0], value_states.shape[-2], -1)
+ )
+ dt_states = torch.exp(self.A * F.softplus(dt_states)).transpose(-1, -2)
+ attn_mask = self.prepare_dynamic_mask(
+ hidden_states=hidden_states,
+ dt_states=dt_states,
+ keep_window_size=self.keep_window_size,
+ attention_mask=attention_mask,
+ )
+ attn_mask = repeat_kv(attn_mask, self.num_key_value_groups)
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ query_states,
+ key_states,
+ value_states,
+ attention_mask=attn_mask,
+ dropout=0.0 if not self.training else self.attention_dropout,
+ scaling=self.scaling,
+ **kwargs,
+ )
+
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
+ attn_output = self.o_proj(attn_output)
+ return attn_output, attn_weights
+
+ def prepare_dynamic_mask(
+ self,
+ hidden_states: torch.Tensor,
+ dt_states: torch.Tensor,
+ keep_window_size: int = 2048,
+ attention_mask: torch.Tensor | None = None,
+ ):
+ """
+ The core idea of DMA is to calculate the dynamic attention mask to mask the tokens that should be masked, so as to form sparse attention.
+
+ Combine `dt_states` with `attention_mask` to generate the final `attn_mask`.
+
+ Args:
+ hidden_states (`torch.Tensor`): The input hidden_states, used to determine the minimum value of the current input precision.
+ dt_states (`torch.Tensor`): dt_states of shape `(batch_size, num_heads, key_sequence_length)`.
+ keep_window_size (`int`): The window size of tokens that are not dynamically masked, and dynamic masking is only performed when the sequence length exceeds this value.
+ attention_mask (`torch.Tensor`, *optional*): attention mask of shape `(batch_size, 1, query_sequence_length, key_sequence_length)`.
+ """
+ min_dtype = torch.finfo(hidden_states.dtype).min
+ dtype = hidden_states.dtype
+ attn_mask = dt_states[:, :, None, :].expand(
+ -1, -1, hidden_states.shape[1], -1
+ ) # [batch_size, num_heads, query_len, key_len]
+ if attention_mask is not None and not isinstance(attention_mask, BlockMask):
+ if attention_mask.dtype == torch.bool:
+ dtype = hidden_states.dtype
+ attention_mask = torch.where(
+ attention_mask, torch.tensor(0.0, device=attention_mask.device, dtype=dtype), min_dtype
+ )
+ attn_mask = attn_mask.masked_fill(attention_mask[:, :, :, : attn_mask.shape[-1]] != 0, min_dtype)
+ if attn_mask.shape[-1] > keep_window_size:
+ active_mask = torch.zeros_like(attn_mask, dtype=dtype, device=attn_mask.device)
+ topk_indices = torch.topk(attn_mask, keep_window_size, dim=-1, largest=True, sorted=False).indices
+ active_mask = active_mask.scatter(-1, topk_indices, 1.0)
+ attn_mask = attn_mask.masked_fill(active_mask == 0.0, min_dtype)
+ return attn_mask
+
+
+class DogeMLP(LlamaMLP):
+ pass
+
+
+class DogeCDMoE(nn.Module):
+ def __init__(self, config: DogeConfig):
+ super().__init__()
+ self.hidden_size = config.hidden_size
+ self.intermediate_size = config.intermediate_size
+ self.act_fn = ACT2FN[config.hidden_act]
+
+ self.num_experts = config.num_experts
+ self.num_keys = math.floor(math.sqrt(self.num_experts))
+ self.top_k = config.num_experts_per_tok
+ self.norm_topk_prob = config.norm_topk_prob
+
+ # shared expert
+ self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=config.mlp_bias)
+ self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=config.mlp_bias)
+ self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=config.mlp_bias)
+
+ # router gate for retrieval experts
+ self.router_gate = nn.Linear(self.hidden_size, self.num_keys * 2, bias=False)
+
+ # routed experts
+ self.down_embed = nn.Embedding(self.num_experts, self.hidden_size)
+ self.up_embed = nn.Embedding(self.num_experts, self.hidden_size)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ **kwargs,
+ ) -> torch.Tensor:
+ bsz, seq_len, _ = hidden_states.shape
+
+ # get routing logits with router gate
+ router_logits = self.router_gate(hidden_states).view(2, bsz * seq_len, -1)
+
+ # get experts with the highest routing logits
+ (scores_x, scores_y), (indices_x, indices_y) = router_logits.topk(self.num_keys, dim=-1)
+ all_scores = scores_x.unsqueeze(-1) + scores_y.unsqueeze(-2)
+ all_indices = indices_x.unsqueeze(-1) * self.num_keys + indices_y.unsqueeze(-2)
+ all_scores = all_scores.view(*all_scores.shape[:-2], -1)
+ all_indices = all_indices.view(*all_indices.shape[:-2], -1)
+ scores, position_indices = all_scores.topk(self.top_k, dim=-1)
+ indices = all_indices.gather(-1, position_indices)
+ routing_weights = F.softmax(scores, dim=-1)
+ if self.norm_topk_prob:
+ routing_weights /= routing_weights.sum(dim=-1, keepdim=True)
+
+ # mix routed experts states with shared expert states
+ down_embed = self.down_embed(indices)
+ up_embed = self.up_embed(indices)
+ experts_weights = torch.matmul(down_embed, hidden_states.view(bsz * seq_len, -1, 1)).view(bsz * seq_len, -1)
+ experts_weights = self.act_fn(experts_weights) * routing_weights
+ experts_states = torch.matmul(experts_weights.view(bsz * seq_len, 1, -1), up_embed).view(bsz, seq_len, -1)
+ hidden_states = self.down_proj(self.act_fn(self.gate_proj(hidden_states)) * self.up_proj(hidden_states))
+ hidden_states = hidden_states + experts_states
+ return hidden_states, router_logits
+
+
+class DogeDecoderLayer(GradientCheckpointingLayer):
+ def __init__(self, config: DogeConfig, layer_idx: int | None = None):
+ super().__init__()
+ self.hidden_dropout = config.hidden_dropout
+
+ self.input_layernorm = DogeRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.self_attn = DogeAttention(config=config, layer_idx=layer_idx)
+ self.input_residual = nn.Parameter(torch.ones(config.hidden_size))
+
+ self.post_attention_layernorm = DogeRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.mlp = DogeMLP(config) if not config.is_moe else DogeCDMoE(config)
+ self.post_attention_residual = nn.Parameter(torch.ones(config.hidden_size))
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ use_cache: bool | None = False,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple[torch.FloatTensor, tuple[torch.FloatTensor, torch.FloatTensor] | None]:
+ # sequence transformation
+ residual = hidden_states
+ hidden_states = self.input_layernorm(hidden_states)
+ hidden_states, self_attn_weights = self.self_attn(
+ hidden_states=hidden_states,
+ position_embeddings=position_embeddings,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ **kwargs,
+ )
+ hidden_states = F.dropout(hidden_states, p=self.hidden_dropout, training=self.training)
+ hidden_states = self.input_residual * residual + hidden_states
+
+ # state transformation
+ residual = hidden_states
+ hidden_states = self.post_attention_layernorm(hidden_states)
+ hidden_states = self.mlp(hidden_states)
+ hidden_states = F.dropout(hidden_states, p=self.hidden_dropout, training=self.training)
+ hidden_states = self.post_attention_residual * residual + hidden_states
+
+ return hidden_states
+
+
+class DogePreTrainedModel(LlamaPreTrainedModel):
+ _supports_flash_attn = False
+ _can_compile_fullgraph = False
+ _can_record_outputs = {
+ "router_logits": OutputRecorder(DogeCDMoE, index=1),
+ "hidden_states": DogeDecoderLayer,
+ "attentions": DogeAttention,
+ }
+
+ @torch.no_grad()
+ def _init_weights(self, module):
+ """Initialize the weights"""
+ PreTrainedModel._init_weights(self, module)
+ if isinstance(module, DogeAttention):
+ if hasattr(module, "A"):
+ init.zeros_(module.A)
+ elif isinstance(module, DogeDecoderLayer):
+ if hasattr(module, "input_residual"):
+ init.ones_(module.input_residual)
+ if hasattr(module, "post_attention_residual"):
+ init.ones_(module.post_attention_residual)
+
+
+class DogeModel(MixtralModel):
+ pass
+
+
+def load_balancing_loss_func(
+ gate_logits: torch.Tensor | tuple[torch.Tensor] | None,
+ num_experts: int | None = None,
+ num_keys: int | None = None,
+ top_k: int = 2,
+ attention_mask: torch.Tensor | None = None,
+) -> torch.Tensor | int:
+ r"""
+ Computes auxiliary load balancing loss as in Switch Transformer - implemented in Pytorch.
+
+ See Switch Transformer (https://huggingface.co/papers/2101.03961) for more details. This function implements the loss
+ function presented in equations (4) - (6) of the paper. It aims at penalizing cases where the routing between
+ experts is too unbalanced.
+
+ Args:
+ gate_logits:
+ Logits from the `router_gate`, should be a tuple of model.config.num_hidden_layers tensors of
+ shape [2, batch_size * sequence_length, num_keys].
+ num_experts:
+ Number of experts
+ num_keys:
+ Number of keys
+ top_k:
+ The number of experts to route per-token, can be also interpreted as the `top-k` routing
+ parameter.
+ attention_mask (`torch.Tensor`, *optional*):
+ The attention_mask used in forward function
+ shape [batch_size X sequence_length] if not None.
+
+ Returns:
+ The auxiliary loss.
+ """
+ if gate_logits is None or not isinstance(gate_logits, tuple):
+ return 0
+
+ compute_dtype = gate_logits[0].dtype
+ compute_device = gate_logits[0].device
+ all_expert_indices = []
+ all_routing_weights = []
+
+ for layer_gate_logits in gate_logits:
+ layer_gate_logits = layer_gate_logits.to(compute_device)
+
+ (scores_x, scores_y), (indices_x, indices_y) = layer_gate_logits.topk(num_keys, dim=-1)
+
+ all_scores = scores_x.unsqueeze(-1) + scores_y.unsqueeze(-2)
+ all_indices = indices_x.unsqueeze(-1) * num_keys + indices_y.unsqueeze(-2)
+ all_scores = all_scores.view(*all_scores.shape[:-2], -1)
+ all_indices = all_indices.view(*all_indices.shape[:-2], -1)
+
+ _, position_indices = all_scores.topk(top_k, dim=-1)
+ expert_indices = all_indices.gather(-1, position_indices)
+
+ routing_weights = F.softmax(all_scores, dim=-1)
+
+ all_expert_indices.append(expert_indices)
+ all_routing_weights.append(routing_weights)
+ all_expert_indices = torch.cat(all_expert_indices, dim=0)
+ all_routing_weights = torch.cat(all_routing_weights, dim=0)
+
+ if attention_mask is None:
+ # Compute the percentage of tokens routed to each experts
+ all_expert_indices = all_expert_indices.view(-1)
+ tokens_per_expert = torch.zeros(num_experts, dtype=compute_dtype, device=compute_device)
+ pad = torch.ones_like(all_expert_indices, dtype=compute_dtype, device=compute_device)
+ tokens_per_expert = tokens_per_expert.scatter_add_(0, all_expert_indices, pad) / all_expert_indices.shape[0]
+
+ # Compute the average probability of routing to these experts
+ router_prob_per_expert = torch.mean(all_routing_weights, dim=0)
+ else:
+ batch_size, sequence_length = attention_mask.shape
+ num_hidden_layers = len(gate_logits)
+
+ # Compute the mask that masks all padding tokens as 0 with the same shape of expert_mask
+ expert_attention_mask = (
+ attention_mask[None, :, :, None]
+ .expand((num_hidden_layers, batch_size, sequence_length, top_k))
+ .reshape(-1)
+ .to(compute_device)
+ )
+ all_expert_indices = all_expert_indices.view(-1)[expert_attention_mask.bool()]
+
+ # Compute the percentage of tokens routed to each experts
+ tokens_per_expert = torch.zeros(num_experts, dtype=compute_dtype, device=compute_device)
+ pad = torch.ones_like(all_expert_indices, dtype=compute_dtype, device=compute_device)
+ tokens_per_expert = tokens_per_expert.scatter_add_(0, all_expert_indices, pad) / torch.sum(
+ expert_attention_mask
+ )
+
+ # Compute the mask that masks all padding tokens as 0 with the same shape of tokens_per_expert
+ router_per_expert_attention_mask = (
+ attention_mask[None, :, :, None]
+ .expand((num_hidden_layers, batch_size, sequence_length, num_experts))
+ .reshape(-1, num_experts)
+ .to(compute_device)
+ )
+
+ # Compute the average probability of routing to these experts
+ router_prob_per_expert = torch.sum(all_routing_weights * router_per_expert_attention_mask, dim=0) / torch.sum(
+ router_per_expert_attention_mask, dim=0
+ )
+
+ overall_loss = torch.sum(tokens_per_expert * router_prob_per_expert)
+ return overall_loss * num_experts
+
+
+class DogeForCausalLM(MixtralForCausalLM):
+ def __init__(self, config):
+ super().__init__(config)
+ self.model = DogeModel(config)
+ self.num_experts = config.num_experts
+
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ output_router_logits: bool | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> MoeCausalLMOutputWithPast:
+ r"""
+ 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]`.
+
+ Example:
+
+ ```python
+ >>> from transformers import AutoTokenizer, DogeForCausalLM
+
+ >>> model = DogeForCausalLM.from_pretrained("SmallDoge/Doge-320M")
+ >>> tokenizer = AutoTokenizer.from_pretrained("SmallDoge/Doge-320M")
+
+ >>> prompt = "Hey, are you conscious? Can you talk to me?"
+ >>> inputs = tokenizer(prompt, return_tensors="pt")
+
+ >>> # Generate
+ >>> generate_ids = model.generate(inputs.input_ids, max_length=30)
+ >>> tokenizer.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]
+ "Hey, are you conscious? Can you talk to me?\nI'm not conscious, but I can talk to you."
+ ```"""
+ output_router_logits = (
+ output_router_logits if output_router_logits is not None else self.config.output_router_logits
+ )
+
+ # decoder outputs consists of (dec_features, layer_state, dec_hidden, dec_attn)
+ outputs: MoeModelOutputWithPast = self.model(
+ input_ids=input_ids,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ hidden_states = outputs.last_hidden_state
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(logits, labels, self.vocab_size, **kwargs)
+
+ aux_loss = None
+ if output_router_logits:
+ aux_loss = load_balancing_loss_func(
+ outputs.router_logits,
+ self.num_experts,
+ math.floor(math.sqrt(self.num_experts)),
+ self.num_experts_per_tok,
+ attention_mask,
+ )
+ if labels is not None:
+ loss += self.router_aux_loss_coef * aux_loss.to(loss.device) # make sure to reside in the same device
+
+ return MoeCausalLMOutputWithPast(
+ loss=loss,
+ aux_loss=aux_loss,
+ logits=logits,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ router_logits=outputs.router_logits,
+ )
+
+
+class DogeForSequenceClassification(LlamaForSequenceClassification):
+ pass
+
+
+__all__ = [
+ "DogeConfig",
+ "DogeForCausalLM",
+ "DogeModel",
+ "DogePreTrainedModel",
+ "DogeForSequenceClassification",
+]
diff --git a/third_party/transformers/src/transformers/models/edgetam_video/__init__.py b/third_party/transformers/src/transformers/models/edgetam_video/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e21eee244be4f7c7069518eeef6f4e4f463599fb
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/edgetam_video/__init__.py
@@ -0,0 +1,28 @@
+# Copyright 2025 the HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_edgetam_video import *
+ from .modeling_edgetam_video import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/edgetam_video/configuration_edgetam_video.py b/third_party/transformers/src/transformers/models/edgetam_video/configuration_edgetam_video.py
new file mode 100644
index 0000000000000000000000000000000000000000..8ce0582b910587a5b83d65e7955912c40e98c666
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/edgetam_video/configuration_edgetam_video.py
@@ -0,0 +1,314 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/edgetam_video/modular_edgetam_video.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_edgetam_video.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2025 the HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...utils import auto_docstring
+from ..auto import CONFIG_MAPPING, AutoConfig
+
+
+@auto_docstring(checkpoint="yonigozlan/EdgeTAM-hf")
+@strict
+class EdgeTamVideoPromptEncoderConfig(PreTrainedConfig):
+ r"""
+ mask_input_channels (`int`, *optional*, defaults to 16):
+ The number of channels to be fed to the `MaskDecoder` module.
+ num_point_embeddings (`int`, *optional*, defaults to 4):
+ The number of point embeddings to be used.
+ scale (`float`, *optional*, defaults to 1):
+ The scale factor for the prompt encoder.
+ """
+
+ base_config_key = "prompt_encoder_config"
+
+ hidden_size: int = 256
+ image_size: int | list[int] | tuple[int, int] = 1024
+ patch_size: int | list[int] | tuple[int, int] = 16
+ mask_input_channels: int = 16
+ num_point_embeddings: int = 4
+ hidden_act: str = "gelu"
+ layer_norm_eps: float = 1e-6
+ scale: int = 1
+
+
+@auto_docstring(checkpoint="yonigozlan/EdgeTAM-hf")
+@strict
+class EdgeTamVideoMaskDecoderConfig(PreTrainedConfig):
+ r"""
+ mlp_dim (`int`, *optional*, defaults to 2048):
+ The dimension of the MLP in the two-way transformer.
+ attention_downsample_rate (`int`, *optional*, defaults to 2):
+ The downsample rate for the attention layers.
+ num_multimask_outputs (`int`, *optional*, defaults to 3):
+ The number of multimask outputs.
+ iou_head_depth (`int`, *optional*, defaults to 3):
+ The depth of the IoU head.
+ iou_head_hidden_dim (`int`, *optional*, defaults to 256):
+ The hidden dimension of the IoU head.
+ dynamic_multimask_via_stability (`bool`, *optional*, defaults to `True`):
+ Whether to use dynamic multimask via stability.
+ dynamic_multimask_stability_delta (`float`, *optional*, defaults to 0.05):
+ The stability delta for the dynamic multimask.
+ dynamic_multimask_stability_thresh (`float`, *optional*, defaults to 0.98):
+ The stability threshold for the dynamic multimask.
+ """
+
+ base_config_key = "mask_decoder_config"
+
+ hidden_size: int = 256
+ hidden_act: str = "gelu"
+ mlp_dim: int = 2048
+ num_hidden_layers: int = 2
+ num_attention_heads: int = 8
+ attention_downsample_rate: int = 2
+ num_multimask_outputs: int = 3
+ iou_head_depth: int = 3
+ iou_head_hidden_dim: int = 256
+ dynamic_multimask_via_stability: bool = True
+ dynamic_multimask_stability_delta: float = 0.05
+ dynamic_multimask_stability_thresh: float = 0.98
+
+
+@auto_docstring(checkpoint="yonigozlan/EdgeTAM-hf")
+@strict
+class EdgeTamVideoConfig(PreTrainedConfig):
+ r"""
+ prompt_encoder_config (Union[`dict`, `EdgeTamVideoPromptEncoderConfig`], *optional*):
+ Dictionary of configuration options used to initialize [`EdgeTamVideoPromptEncoderConfig`].
+ mask_decoder_config (Union[`dict`, `EdgeTamVideoMaskDecoderConfig`], *optional*):
+ Dictionary of configuration options used to initialize [`EdgeTamMaskDecoderConfig`].
+ num_maskmem (`int`, *optional*, defaults to 7):
+ The number of memory slots for the mask memory.
+ sigmoid_scale_for_mem_enc (`float`, *optional*, defaults to 20.0):
+ Scale factor for the sigmoid function in the memory encoder.
+ sigmoid_bias_for_mem_enc (`float`, *optional*, defaults to -10.0):
+ Bias for the sigmoid function in the memory encoder.
+ enable_occlusion_spatial_embedding (`bool`, *optional*, defaults to `True`):
+ Whether to enable spatial embedding for occlusions.
+ multimask_output_in_sam (`bool`, *optional*, defaults to `True`):
+ Whether to output multiple masks from the SAM head.
+ multimask_min_pt_num (`int`, *optional*, defaults to 0):
+ The minimum number of points to trigger multimask output.
+ multimask_max_pt_num (`int`, *optional*, defaults to 1):
+ The maximum number of points to trigger multimask output.
+ multimask_output_for_tracking (`bool`, *optional*, defaults to `True`):
+ Whether to use multimask output for tracking.
+ max_object_pointers_in_encoder (`int`, *optional*, defaults to 16):
+ The maximum number of object pointers in the encoder.
+ max_cond_frame_num (`int`, *optional*, defaults to -1):
+ Maximum number of conditioning frames to use in memory attention. Set to -1 to use all conditioning frames.
+ enable_temporal_pos_encoding_for_object_pointers (`bool`, *optional*, defaults to `True`):
+ Whether to enable temporal positional encoding for object pointers.
+ memory_attention_hidden_size (`int`, *optional*, defaults to 256):
+ Dimensionality of the memory attention hidden states.
+ memory_attention_num_layers (`int`, *optional*, defaults to 2):
+ The number of layers in the memory attention module.
+ memory_attention_num_attention_heads (`int`, *optional*, defaults to 1):
+ Number of attention heads for each attention layer in the memory attention.
+ memory_attention_downsample_rate (`int`, *optional*, defaults to 1):
+ The downsample rate for the attention layers.
+ memory_attention_mlp_hidden_size (`int`, *optional*, defaults to 2048):
+ The dimension of the feedforward network in the memory attention module.
+ memory_attention_mlp_hidden_act (`str`, *optional*, defaults to `"relu"`):
+ The non-linear activation function in the feedforward network in the memory attention module.
+ memory_attention_dropout (`float`, *optional*, defaults to 0.1):
+ The dropout rate for the memory attention module.
+ memory_attention_rope_theta (`float`, *optional*, defaults to 10000):
+ The Rope theta parameter.
+ memory_attention_rope_feat_sizes (`Tuple[int, int]`, *optional*, defaults to `[64, 64]`):
+ The feature sizes for the Rope positional encoding.
+ memory_attention_rope_k_sizes (`List[int]`, *optional*, defaults to `[16, 16]`):
+ The key feature sizes for the RoPE positional encoding in memory attention.
+ memory_attention_rope_dropout (`float`, *optional*, defaults to 0.1):
+ The dropout rate for the Rope positional encoding.
+ perceiver_resampler_num_latents (`int`, *optional*, defaults to 256):
+ The number of 1D latent tokens in the perceiver resampler.
+ perceiver_resampler_num_latents_2d (`int`, *optional*, defaults to 256):
+ The number of 2D latent tokens in the perceiver resampler.
+ perceiver_resampler_hidden_size (`int`, *optional*, defaults to 64):
+ The hidden size of the perceiver resampler.
+ perceiver_resampler_mlp_intermediate_size (`int`, *optional*, defaults to 256):
+ The intermediate size of the feedforward network in the perceiver resampler.
+ perceiver_resampler_num_attention_heads (`int`, *optional*, defaults to 1):
+ The number of attention heads in the perceiver resampler.
+ perceiver_resampler_attention_head_dim (`int`, *optional*, defaults to 64):
+ The dimension of each attention head in the perceiver resampler.
+ perceiver_resampler_num_layers (`int`, *optional*, defaults to 2):
+ The number of layers in the perceiver resampler.
+ perceiver_resampler_hidden_dropout (`float`, *optional*, defaults to 0.0):
+ The dropout rate for the hidden layers in the perceiver resampler.
+ perceiver_resampler_attention_dropout (`float`, *optional*, defaults to 0.0):
+ The dropout rate for the attention layers in the perceiver resampler.
+ memory_encoder_hidden_size (`int`, *optional*, defaults to 256):
+ Dimensionality of the memory encoder hidden states.
+ memory_encoder_output_channels (`int`, *optional*, defaults to 64):
+ The number of output channels for the memory encoder.
+ mask_downsampler_embed_dim (`int`, *optional*, defaults to 256):
+ The dimension of the mask downsampler embedding.
+ memory_fuser_intermediate_dim (`int`, *optional*, defaults to 1024):
+ The intermediate dimension of the memory fuser feedforward network.
+ mask_downsampler_kernel_size (`int`, *optional*, defaults to 3):
+ The kernel size for the mask downsampler.
+ mask_downsampler_stride (`int`, *optional*, defaults to 2):
+ The stride for the mask downsampler.
+ mask_downsampler_padding (`int`, *optional*, defaults to 1):
+ The padding for the mask downsampler.
+ mask_downsampler_total_stride (`int`, *optional*, defaults to 16):
+ The total stride for the mask downsampler.
+ mask_downsampler_hidden_act (`str`, *optional*, defaults to `"gelu"`):
+ The non-linear activation function in the mask downsampler.
+ memory_fuser_num_layers (`int`, *optional*, defaults to 2):
+ The number of layers in the memory fuser.
+ memory_fuser_embed_dim (`int`, *optional*, defaults to 256):
+ The dimension of the memory fuser embedding.
+ memory_fuser_kernel_size (`int`, *optional*, defaults to 7):
+ The kernel size for the memory fuser.
+ memory_fuser_padding (`int`, *optional*, defaults to 3):
+ The padding for the memory fuser.
+ memory_fuser_layer_scale_init_value (`float`, *optional*, defaults to 1e-06):
+ The initial value for the layer scale in the memory fuser.
+ memory_fuser_hidden_act (`str`, *optional*, defaults to `"gelu"`):
+ The non-linear activation function in the memory fuser.
+
+ Example:
+
+ ```python
+ >>> from transformers import (
+ ... EdgeTamVisionConfig,
+ ... EdgeTamVideoPromptEncoderConfig,
+ ... EdgeTamVideoMaskDecoderConfig,
+ ... EdgeTamVideoModel,
+ ... EdgeTamVideoConfig,
+ ... )
+
+ >>> # Initializing a EdgeTamVideoConfig with `"facebook/edgetam.1_hiera_tiny"` style configuration
+ >>> configuration = EdgeTamVideoConfig()
+
+ >>> # Initializing a EdgeTamVideoModel (with random weights) from the `"facebook/edgetam.1_hiera_tiny"` style configuration
+ >>> model = EdgeTamVideoModel(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+
+ >>> # We can also initialize a EdgeTamConfig from a EdgeTamVisionConfig, EdgeTamPromptEncoderConfig, and EdgeTamMaskDecoderConfig
+
+ >>> # Initializing EDGETAM vision encoder, memory attention, and memory encoder configurations
+ >>> vision_config = EdgeTamVisionConfig()
+ >>> prompt_encoder_config = EdgeTamVideoPromptEncoderConfig()
+ >>> mask_decoder_config = EdgeTamVideoMaskDecoderConfig()
+
+ >>> config = EdgeTamVideoConfig(vision_config, prompt_encoder_config, mask_decoder_config)
+ ```"""
+
+ model_type = "edgetam_video"
+ sub_configs = {
+ "vision_config": AutoConfig,
+ "prompt_encoder_config": EdgeTamVideoPromptEncoderConfig,
+ "mask_decoder_config": EdgeTamVideoMaskDecoderConfig,
+ }
+
+ vision_config: dict | PreTrainedConfig | None = None
+ prompt_encoder_config: dict | PreTrainedConfig | None = None
+ mask_decoder_config: dict | PreTrainedConfig | None = None
+ initializer_range: float = 0.02
+ num_maskmem: int = 7
+ image_size: int | list[int] | tuple[int, int] = 1024
+ sigmoid_scale_for_mem_enc: float = 20.0
+ sigmoid_bias_for_mem_enc: float = -10.0
+ enable_occlusion_spatial_embedding: bool = True
+ multimask_output_in_sam: bool = True
+ multimask_min_pt_num: int = 0
+ multimask_max_pt_num: int = 1
+ multimask_output_for_tracking: bool = True
+ max_object_pointers_in_encoder: int = 16
+ max_cond_frame_num: int = -1
+ enable_temporal_pos_encoding_for_object_pointers: bool = True
+
+ # memory attention
+ memory_attention_hidden_size: int = 256
+ memory_attention_num_layers: int = 2
+ memory_attention_num_attention_heads: int = 1
+ memory_attention_downsample_rate: int = 1
+ memory_attention_mlp_hidden_size: int = 2048
+ memory_attention_mlp_hidden_act: str = "relu"
+ memory_attention_dropout: float | int = 0.1
+ memory_attention_rope_theta: float | int = 10000
+ memory_attention_rope_feat_sizes: list | None = None
+ memory_attention_rope_k_sizes: list | None = None
+ memory_attention_rope_dropout: float | int = 0.1
+
+ # spatial perceiver resampler
+ perceiver_resampler_num_latents: int = 256
+ perceiver_resampler_num_latents_2d: int = 256
+ perceiver_resampler_hidden_size: int = 64
+ perceiver_resampler_mlp_intermediate_size: int = 256
+ perceiver_resampler_num_attention_heads: int = 1
+ perceiver_resampler_attention_head_dim: int = 64
+ perceiver_resampler_num_layers: int = 2
+ perceiver_resampler_hidden_dropout: float | int = 0.0
+ perceiver_resampler_attention_dropout: float | int = 0.0
+
+ # memory encoder
+ memory_encoder_hidden_size: int = 256
+ memory_encoder_output_channels: int = 64
+ mask_downsampler_embed_dim: int = 256
+ memory_fuser_intermediate_dim: int = 1024
+ mask_downsampler_kernel_size: int = 3
+ mask_downsampler_stride: int = 2
+ mask_downsampler_padding: int = 1
+ mask_downsampler_total_stride: int = 16
+ mask_downsampler_hidden_act: str = "gelu"
+ memory_fuser_num_layers: int = 2
+ memory_fuser_embed_dim: int = 256
+ memory_fuser_kernel_size: int = 7
+ memory_fuser_padding: int = 3
+ memory_fuser_layer_scale_init_value: float = 1e-6
+ memory_fuser_hidden_act: str = "gelu"
+
+ def __post_init__(self, **kwargs):
+ self.prompt_encoder_config = self.prompt_encoder_config if self.prompt_encoder_config is not None else {}
+ self.mask_decoder_config = self.mask_decoder_config if self.mask_decoder_config is not None else {}
+ self.memory_attention_rope_feat_sizes = (
+ [64, 64] if self.memory_attention_rope_feat_sizes is None else self.memory_attention_rope_feat_sizes
+ )
+ self.memory_attention_rope_k_sizes = (
+ [16, 16] if self.memory_attention_rope_k_sizes is None else self.memory_attention_rope_k_sizes
+ )
+
+ if isinstance(self.vision_config, dict):
+ self.vision_config["model_type"] = self.vision_config.get("model_type", "sam2_vision_model")
+ self.vision_config = CONFIG_MAPPING[self.vision_config["model_type"]](**self.vision_config)
+ elif self.vision_config is None:
+ self.vision_config = CONFIG_MAPPING["sam2_vision_model"]()
+
+ if isinstance(self.prompt_encoder_config, dict):
+ self.prompt_encoder_config = EdgeTamVideoPromptEncoderConfig(**self.prompt_encoder_config)
+ elif self.prompt_encoder_config is None:
+ self.prompt_encoder_config = EdgeTamVideoPromptEncoderConfig()
+
+ if isinstance(self.mask_decoder_config, dict):
+ self.mask_decoder_config = EdgeTamVideoMaskDecoderConfig(**self.mask_decoder_config)
+ elif self.mask_decoder_config is None:
+ self.mask_decoder_config = EdgeTamVideoMaskDecoderConfig()
+ super().__post_init__(**kwargs)
+
+
+__all__ = ["EdgeTamVideoMaskDecoderConfig", "EdgeTamVideoPromptEncoderConfig", "EdgeTamVideoConfig"]
diff --git a/third_party/transformers/src/transformers/models/edgetam_video/convert_edgetam_video_to_hf.py b/third_party/transformers/src/transformers/models/edgetam_video/convert_edgetam_video_to_hf.py
new file mode 100644
index 0000000000000000000000000000000000000000..293ebcaea2da03c5da783232eb8439512ec35b26
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/edgetam_video/convert_edgetam_video_to_hf.py
@@ -0,0 +1,321 @@
+# Copyright 2025 The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Convert SAM checkpoints from the original repository.
+
+URL: https://github.com/facebookresearch/segment-anything-2.
+"""
+
+import argparse
+import re
+from io import BytesIO
+
+import httpx
+import numpy as np
+import torch
+from huggingface_hub import hf_hub_download
+from PIL import Image
+
+from transformers import (
+ EdgeTamVideoConfig,
+ EdgeTamVideoMaskDecoderConfig,
+ EdgeTamVideoModel,
+ EdgeTamVideoPromptEncoderConfig,
+ EdgeTamVisionConfig,
+ Sam2ImageProcessorFast,
+ Sam2VideoProcessor,
+ Sam2VideoVideoProcessor,
+ TimmWrapperConfig,
+)
+
+
+def get_config(model_name):
+ backbone_config = TimmWrapperConfig.from_pretrained(
+ "timm/repvit_m1.dist_in1k",
+ model_args={"in_chans": 3, "features_only": True, "out_indices": (0, 1, 2, 3)},
+ )
+ vision_config = EdgeTamVisionConfig(backbone_config=backbone_config)
+
+ prompt_encoder_config = EdgeTamVideoPromptEncoderConfig()
+ mask_decoder_config = EdgeTamVideoMaskDecoderConfig()
+ enable_temporal_pos_encoding_for_object_pointers = False
+ enable_occlusion_spatial_embedding = False
+
+ config = EdgeTamVideoConfig(
+ vision_config=vision_config,
+ prompt_encoder_config=prompt_encoder_config,
+ mask_decoder_config=mask_decoder_config,
+ enable_temporal_pos_encoding_for_object_pointers=enable_temporal_pos_encoding_for_object_pointers,
+ enable_occlusion_spatial_embedding=enable_occlusion_spatial_embedding,
+ )
+
+ return config
+
+
+KEYS_TO_MODIFY_MAPPING = {
+ "iou_prediction_head.layers.0": "iou_prediction_head.proj_in",
+ "iou_prediction_head.layers.1": "iou_prediction_head.layers.0",
+ "iou_prediction_head.layers.2": "iou_prediction_head.proj_out",
+ "mask_decoder.output_upscaling.0": "mask_decoder.upscale_conv1",
+ "mask_decoder.output_upscaling.1": "mask_decoder.upscale_layer_norm",
+ "mask_decoder.output_upscaling.3": "mask_decoder.upscale_conv2",
+ "mask_downscaling.0": "mask_embed.conv1",
+ "mask_downscaling.1": "mask_embed.layer_norm1",
+ "mask_downscaling.3": "mask_embed.conv2",
+ "mask_downscaling.4": "mask_embed.layer_norm2",
+ "mask_downscaling.6": "mask_embed.conv3",
+ "dwconv": "depthwise_conv",
+ "pwconv": "pointwise_conv",
+ "fuser": "memory_fuser",
+ "point_embeddings": "point_embed",
+ "pe_layer.positional_encoding_gaussian_matrix": "shared_embedding.positional_embedding",
+ "obj_ptr_tpos_proj": "temporal_positional_encoding_projection_layer",
+ "no_obj_embed_spatial": "occlusion_spatial_embedding_parameter",
+ "sam_prompt_encoder": "prompt_encoder",
+ "sam_mask_decoder": "mask_decoder",
+ "maskmem_tpos_enc": "memory_temporal_positional_encoding",
+ "gamma": "scale",
+ "image_encoder.neck": "vision_encoder.neck",
+ "image_encoder": "vision_encoder.backbone",
+ "neck.0": "neck.conv1",
+ "neck.1": "neck.layer_norm1",
+ "neck.2": "neck.conv2",
+ "neck.3": "neck.layer_norm2",
+ "pix_feat_proj": "feature_projection",
+ "patch_embed.proj": "patch_embed.projection",
+ "no_mem_embed": "no_memory_embedding",
+ "no_mem_pos_enc": "no_memory_positional_encoding",
+ "obj_ptr": "object_pointer",
+ ".norm": ".layer_norm",
+ "trunk.": "",
+ "out_proj": "o_proj",
+ "body.": "timm_model.",
+ "ff.0": "mlp.layer_norm",
+ "ff.1": "mlp.up_proj",
+ "ff.3": "mlp.down_proj",
+}
+
+
+def replace_keys(state_dict):
+ model_state_dict = {}
+ output_hypernetworks_mlps_pattern = r".*.output_hypernetworks_mlps.(\d+).layers.(\d+).*"
+ output_mask_decoder_mlps_pattern = r"mask_decoder.transformer.layers.(\d+).mlp.layers.(\d+).*"
+ output_mask_decoder_score_head_pattern = r"mask_decoder.pred_obj_score_head.layers.(\d+).*"
+ output_vision_encoder_mlps_pattern = r"vision_encoder.backbone.blocks.(\d+).mlp.layers.(\d+).*"
+ output_vision_encoder_neck_pattern = r"vision_encoder.neck.convs.(\d+).conv"
+ output_memory_encoder_projection_pattern = r"memory_encoder.o_proj.*"
+ memory_attention_pattern = r"memory_attention.*"
+ output_object_pointer_proj_pattern = r"object_pointer_proj.layers.(\d+).*"
+ output_memory_encoder_mask_downsampler_pattern = r"memory_encoder.mask_downsampler.encoder.(\d+).*"
+ perceiver_resampler_patterns = {
+ r"spatial_perceiver.latents": r"spatial_perceiver.latents_1d",
+ r"spatial_perceiver.latents_1d_2d": r"spatial_perceiver.latents_2d",
+ r"spatial_perceiver.layers.(\d+).attn.layer_norm_x": r"spatial_perceiver.layers.\1.layer_norm_input",
+ r"spatial_perceiver.layers.(\d+).attn.layer_norm_latents": r"spatial_perceiver.layers.\1.layer_norm_latents",
+ r"spatial_perceiver.layers.(\d+).self_attn.layer_norm": r"spatial_perceiver.layers.\1.layer_norm_self",
+ r"spatial_perceiver.layers.(\d+).attn.to_q": r"spatial_perceiver.layers.\1.cross_attention.q_proj",
+ r"spatial_perceiver.layers.(\d+).attn.to_kv": r"spatial_perceiver.layers.\1.cross_attention.kv_proj_combined",
+ r"spatial_perceiver.layers.(\d+).attn.to_out": r"spatial_perceiver.layers.\1.cross_attention.o_proj",
+ r"spatial_perceiver.layers.(\d+).self_attn.to_q": r"spatial_perceiver.layers.\1.self_attention.q_proj",
+ r"spatial_perceiver.layers.(\d+).self_attn.to_kv": r"spatial_perceiver.layers.\1.self_attention.kv_proj_combined",
+ r"spatial_perceiver.layers.(\d+).self_attn.to_out": r"spatial_perceiver.layers.\1.self_attention.o_proj",
+ r"spatial_perceiver.layers.(\d+).attn": r"spatial_perceiver.layers.\1.cross_attention",
+ r"spatial_perceiver.layers.(\d+).self_attn": r"spatial_perceiver.layers.\1.self_attention",
+ }
+
+ for key, value in state_dict.items():
+ for key_to_modify, new_key in KEYS_TO_MODIFY_MAPPING.items():
+ if key_to_modify in key:
+ key = key.replace(key_to_modify, new_key)
+
+ for pattern, replacement in perceiver_resampler_patterns.items():
+ if re.match(pattern, key):
+ key = re.sub(pattern, replacement, key)
+
+ # vision_encoder.blocks.0.mlp.layers.1.weight -> vision_encoder.blocks.0.mlp.proj_out.weight
+ if re.match(output_vision_encoder_mlps_pattern, key):
+ layer_nb = int(re.match(output_vision_encoder_mlps_pattern, key).group(2))
+ if layer_nb == 0:
+ key = key.replace("layers.0", "proj_in")
+ elif layer_nb == 1:
+ key = key.replace("layers.1", "proj_out")
+
+ if re.match(memory_attention_pattern, key):
+ key = key.replace("linear1", "mlp.up_proj")
+ key = key.replace("linear2", "mlp.down_proj")
+
+ # mask_decoder.transformer.layers.0.mlp.layers.1.weight -> mask_decoder.transformer.layers.1.mlp.proj_out.weight
+ if re.match(output_mask_decoder_mlps_pattern, key):
+ layer_nb = int(re.match(output_mask_decoder_mlps_pattern, key).group(2))
+ if layer_nb == 0:
+ key = key.replace("mlp.layers.0", "mlp.proj_in")
+ elif layer_nb == 1:
+ key = key.replace("mlp.layers.1", "mlp.proj_out")
+
+ # mask_decoder.pred_obj_score_head.layers.1.weight -> mask_decoder.pred_obj_score_head.proj_in.weight
+ if re.match(output_mask_decoder_score_head_pattern, key):
+ layer_nb = int(re.match(output_mask_decoder_score_head_pattern, key).group(1))
+ if layer_nb == 0:
+ key = key.replace("layers.0", "proj_in")
+ elif layer_nb == 1:
+ key = key.replace("layers.1", "layers.0")
+ elif layer_nb == 2:
+ key = key.replace("layers.2", "proj_out")
+
+ if re.match(output_hypernetworks_mlps_pattern, key):
+ layer_nb = int(re.match(output_hypernetworks_mlps_pattern, key).group(2))
+ if layer_nb == 0:
+ key = key.replace("layers.0", "proj_in")
+ elif layer_nb == 1:
+ key = key.replace("layers.1", "layers.0")
+ elif layer_nb == 2:
+ key = key.replace("layers.2", "proj_out")
+
+ # vision_encoder.neck.convs.1.conv.bias -> vision_encoder.neck.convs.1.bias
+ if re.match(output_vision_encoder_neck_pattern, key):
+ key = key.replace(".conv.", ".")
+
+ # memory_encoder.o_proj.weight -> memory_encoder.projection.weight
+ if re.match(output_memory_encoder_projection_pattern, key):
+ key = key.replace(".o_proj.", ".projection.")
+
+ if re.match(output_object_pointer_proj_pattern, key):
+ layer_nb = int(re.match(output_object_pointer_proj_pattern, key).group(1))
+ if layer_nb == 0:
+ key = key.replace("layers.0", "proj_in")
+ elif layer_nb == 1:
+ key = key.replace("layers.1", "layers.0")
+ elif layer_nb == 2:
+ key = key.replace("layers.2", "proj_out")
+
+ key = key.replace("layers.2", "proj_out")
+
+ if re.match(output_memory_encoder_mask_downsampler_pattern, key):
+ layer_nb = int(re.match(output_memory_encoder_mask_downsampler_pattern, key).group(1))
+ if layer_nb == 12:
+ key = key.replace(f"encoder.{layer_nb}", "final_conv")
+ elif layer_nb % 3 == 0:
+ key = key.replace(f"encoder.{layer_nb}", f"layers.{layer_nb // 3}.conv")
+ elif layer_nb % 3 == 1:
+ key = key.replace(f"encoder.{layer_nb}", f"layers.{layer_nb // 3}.layer_norm")
+ if "kv_proj_combined" in key:
+ # Split the weight tensor in half along dimension 0 (output dimension)
+ k_weight, v_weight = torch.chunk(value, 2, dim=0)
+ # Create the k_proj and v_proj keys
+ k_key = key.replace("kv_proj_combined", "k_proj")
+ v_key = key.replace("kv_proj_combined", "v_proj")
+ model_state_dict[k_key] = k_weight
+ model_state_dict[v_key] = v_weight
+ continue
+
+ model_state_dict[key] = value
+
+ model_state_dict["shared_image_embedding.positional_embedding"] = model_state_dict[
+ "prompt_encoder.shared_embedding.positional_embedding"
+ ]
+ model_state_dict["prompt_encoder.point_embed.weight"] = torch.cat(
+ [model_state_dict.pop(f"prompt_encoder.point_embed.{i}.weight") for i in range(4)],
+ dim=0,
+ )
+
+ return model_state_dict
+
+
+def convert_edgetam_checkpoint(model_name, checkpoint_path, pytorch_dump_folder, push_to_hub, run_sanity_check):
+ config = get_config(model_name)
+
+ state_dict = torch.load(checkpoint_path, map_location="cpu")["model"]
+ state_dict = replace_keys(state_dict)
+
+ image_processor = Sam2ImageProcessorFast()
+ video_processor = Sam2VideoVideoProcessor()
+ processor = Sam2VideoProcessor(image_processor=image_processor, video_processor=video_processor)
+ hf_model = EdgeTamVideoModel(config)
+ hf_model.eval()
+
+ device = "cuda" if torch.cuda.is_available() else "cpu"
+
+ missing_keys, unexpected_keys = hf_model.load_state_dict(state_dict, strict=True)
+ hf_model = hf_model.to(device)
+ print("Missing keys:", missing_keys)
+ print("Unexpected keys:", unexpected_keys)
+
+ if run_sanity_check:
+ url = "https://huggingface.co/ybelkada/segment-anything/resolve/main/assets/car.png"
+ with httpx.stream("GET", url) as response:
+ raw_image = Image.open(BytesIO(response.read())).convert("RGB")
+
+ input_points = [[[[1000, 600]]]]
+ input_labels = [[[1]]]
+
+ inputs = processor(
+ images=np.array(raw_image), input_points=input_points, input_labels=input_labels, return_tensors="pt"
+ ).to(device)
+
+ with torch.no_grad():
+ output = hf_model._single_frame_forward(**inputs)
+ scores = output.iou_scores.squeeze()
+
+ assert torch.allclose(scores, torch.tensor([0.0356, 0.2141, 0.9707]).cuda(), atol=1e-3)
+
+ if pytorch_dump_folder is not None:
+ processor.save_pretrained(pytorch_dump_folder)
+ hf_model.save_pretrained(pytorch_dump_folder)
+
+ if push_to_hub:
+ repo_id = f"yonigozlan/{pytorch_dump_folder.split('/')[-1]}"
+ processor.push_to_hub(repo_id)
+ hf_model.push_to_hub(repo_id)
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ choices = ["EdgeTAM"]
+ parser.add_argument(
+ "--model_name",
+ default="EdgeTAM",
+ choices=choices,
+ type=str,
+ help="Name of the original model to convert",
+ )
+ parser.add_argument(
+ "--checkpoint_path",
+ type=str,
+ required=False,
+ help="Path to the original checkpoint",
+ )
+ parser.add_argument("--pytorch_dump_folder_path", default="", type=str, help="Path to the output PyTorch model.")
+ parser.add_argument(
+ "--push_to_hub",
+ action="store_true",
+ help="Whether to push the model and processor to the hub after converting",
+ )
+ parser.add_argument(
+ "--run_sanity_check",
+ action="store_true",
+ help="Whether to run the sanity check after converting",
+ )
+
+ args = parser.parse_args()
+
+ hf_model_name = args.model_name.replace("_", "-")
+ checkpoint_path = (
+ hf_hub_download(f"facebook/{hf_model_name}", f"{args.model_name.lower()}.pt")
+ if args.checkpoint_path is None
+ else args.checkpoint_path
+ )
+
+ convert_edgetam_checkpoint(
+ args.model_name, checkpoint_path, args.pytorch_dump_folder_path, args.push_to_hub, args.run_sanity_check
+ )
diff --git a/third_party/transformers/src/transformers/models/edgetam_video/modeling_edgetam_video.py b/third_party/transformers/src/transformers/models/edgetam_video/modeling_edgetam_video.py
new file mode 100644
index 0000000000000000000000000000000000000000..89a72e6c88b5816bf09606db854e22292deba133
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/edgetam_video/modeling_edgetam_video.py
@@ -0,0 +1,3127 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/edgetam_video/modular_edgetam_video.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_edgetam_video.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2025 the HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import math
+from collections import OrderedDict
+from collections.abc import Callable, Iterator
+from dataclasses import dataclass
+from typing import Any
+
+import numpy as np
+import torch
+import torch.nn as nn
+import torch.nn.functional as F
+from torch import Tensor
+from tqdm import tqdm
+
+from ... import initialization as init
+from ...activations import ACT2FN
+from ...modeling_flash_attention_utils import FlashAttentionKwargs
+from ...modeling_layers import GradientCheckpointingLayer
+from ...modeling_outputs import BaseModelOutput, BaseModelOutputWithPooling
+from ...modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
+from ...processing_utils import Unpack
+from ...pytorch_utils import compile_compatible_method_lru_cache
+from ...utils import ModelOutput, auto_docstring, can_return_tuple, logging
+from ...utils.generic import TransformersKwargs, is_flash_attention_requested
+from ...utils.output_capturing import OutputRecorder
+from ..auto import AutoModel
+from .configuration_edgetam_video import (
+ EdgeTamVideoConfig,
+ EdgeTamVideoMaskDecoderConfig,
+ EdgeTamVideoPromptEncoderConfig,
+)
+
+
+logger = logging.get_logger(__name__)
+
+
+class EdgeTamVideoLayerNorm(nn.LayerNorm):
+ r"""LayerNorm that supports two data formats: channels_last (default) or channels_first.
+ The ordering of the dimensions in the inputs. channels_last corresponds to inputs with shape (batch_size, height,
+ width, channels) while channels_first corresponds to inputs with shape (batch_size, channels, height, width).
+ """
+
+ def __init__(self, normalized_shape, *, eps=1e-6, data_format="channels_last", **kwargs):
+ super().__init__(normalized_shape, eps=eps, **kwargs)
+ if data_format not in ["channels_last", "channels_first"]:
+ raise NotImplementedError(f"Unsupported data format: {data_format}")
+ self.data_format = data_format
+
+ def forward(self, features: torch.Tensor) -> torch.Tensor:
+ """
+ Args:
+ features: Tensor of shape (batch_size, channels, height, width) OR (batch_size, height, width, channels)
+ """
+ if self.data_format == "channels_first":
+ features = features.permute(0, 2, 3, 1)
+ features = super().forward(features)
+ features = features.permute(0, 3, 1, 2)
+ else:
+ features = super().forward(features)
+ return features
+
+
+# Lightly adapted from ConvNext (https://github.com/facebookresearch/ConvNeXt)
+class EdgeTamVideoMemoryFuserCXBlock(GradientCheckpointingLayer):
+ def __init__(self, config: EdgeTamVideoConfig):
+ super().__init__()
+ self.depthwise_conv = nn.Conv2d(
+ config.memory_fuser_embed_dim,
+ config.memory_fuser_embed_dim,
+ kernel_size=config.memory_fuser_kernel_size,
+ padding=config.memory_fuser_padding,
+ groups=config.memory_fuser_embed_dim,
+ ) # depthwise conv
+ self.layer_norm = EdgeTamVideoLayerNorm(config.memory_fuser_embed_dim, eps=1e-6, data_format="channels_first")
+ self.activation = ACT2FN[config.memory_fuser_hidden_act]
+ self.pointwise_conv1 = nn.Linear(
+ config.memory_fuser_embed_dim, config.memory_fuser_intermediate_dim
+ ) # pointwise/1x1 convs, implemented with linear layers
+ self.pointwise_conv2 = nn.Linear(config.memory_fuser_intermediate_dim, config.memory_fuser_embed_dim)
+ self.scale = nn.Parameter(
+ config.memory_fuser_layer_scale_init_value * torch.ones(config.memory_fuser_embed_dim),
+ requires_grad=True,
+ )
+
+ def forward(self, hidden_states):
+ input = hidden_states
+ hidden_states = self.depthwise_conv(hidden_states)
+ hidden_states = self.layer_norm(hidden_states)
+ hidden_states = hidden_states.permute(0, 2, 3, 1) # (N, C, H, W) -> (N, H, W, C)
+ hidden_states = self.pointwise_conv1(hidden_states)
+ hidden_states = self.activation(hidden_states)
+ hidden_states = self.pointwise_conv2(hidden_states)
+ hidden_states = self.scale * hidden_states
+ hidden_states = hidden_states.permute(0, 3, 1, 2) # (N, H, W, C) -> (N, C, H, W)
+
+ hidden_states = input + hidden_states
+ return hidden_states
+
+
+@dataclass
+@auto_docstring(custom_intro="Base class for the vision encoder's outputs.")
+class EdgeTamVideoVisionEncoderOutput(BaseModelOutputWithPooling):
+ r"""
+ last_hidden_state (`torch.FloatTensor` of shape `(batch_size, height, width, hidden_size)`):
+ Sequence of hidden-states at the output of the last layer of the model.
+ hidden_states (`tuple(torch.FloatTensor)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
+ Tuple of `torch.FloatTensor` (one for the output of the embeddings, if the model has an embedding layer, +
+ one for the output of each stage) of shape `(batch_size, height, width, hidden_size)`. Hidden-states of the
+ model at the output of each stage.
+ attentions (`tuple(torch.FloatTensor)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`):
+ Tuple of `torch.FloatTensor` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
+ sequence_length)`. Attentions weights after the attention softmax, used to compute the weighted average in
+ the self-attention heads.
+ fpn_hidden_states (`tuple(torch.FloatTensor)`):
+ Tuple of `torch.FloatTensor` (one for each feature level, from high to low resolution) of shape
+ `(batch_size, hidden_size, height, width)`. Feature maps from the Feature Pyramid Network neck.
+ fpn_position_encoding (`tuple(torch.FloatTensor)`):
+ Tuple of `torch.FloatTensor` (one for each feature level, from high to low resolution) of shape
+ `(batch_size, hidden_size, height, width)`. Positional encodings corresponding to the `fpn_hidden_states`.
+ """
+
+ fpn_hidden_states: torch.FloatTensor | None = None
+ fpn_position_encoding: torch.FloatTensor | None = None
+
+
+class EdgeTamVideoVisionRotaryEmbedding(nn.Module):
+ """
+ Vision Rotary Position Embedding for SAM2, following transformers library standards.
+ Supports 2D (axial) rotary embeddings for spatial dimensions.
+ """
+
+ def __init__(self, config: EdgeTamVideoConfig, end_x: int | None = None, end_y: int | None = None):
+ super().__init__()
+ self.dim = config.memory_attention_hidden_size // (
+ config.memory_attention_downsample_rate * config.memory_attention_num_attention_heads
+ )
+ # Ensure even dimension for proper axial splitting
+ if self.dim % 4 != 0:
+ raise ValueError("Dimension must be divisible by 4 for axial RoPE")
+ self.end_x, self.end_y = config.memory_attention_rope_feat_sizes if end_x is None else (end_x, end_y)
+ self.memory_attention_rope_theta = config.memory_attention_rope_theta
+
+ # directly register the cos and sin embeddings as we have a fixed feature shape
+ inv_freq = self.create_inv_freq()
+ self.register_buffer("rope_embeddings_cos", inv_freq.cos(), persistent=False)
+ self.register_buffer("rope_embeddings_sin", inv_freq.sin(), persistent=False)
+
+ @torch.no_grad()
+ def forward(self) -> tuple[torch.Tensor, torch.Tensor]:
+ # As the feature map size is fixed, we can just return the pre-computed embeddings.
+ return self.rope_embeddings_cos, self.rope_embeddings_sin
+
+ def create_inv_freq(self):
+ freqs = 1.0 / (
+ self.memory_attention_rope_theta ** (torch.arange(0, self.dim, 4)[: (self.dim // 4)].float() / self.dim)
+ )
+ # Generate 2D position indices for axial rotary embedding
+ flattened_indices = torch.arange(self.end_x * self.end_y, dtype=torch.long)
+ x_positions = flattened_indices % self.end_x
+ y_positions = torch.div(flattened_indices, self.end_x, rounding_mode="floor")
+ freqs_x = torch.outer(x_positions, freqs).float()
+ freqs_y = torch.outer(y_positions, freqs).float()
+ inv_freq = torch.cat([freqs_x, freqs_y], dim=-1)
+ inv_freq = inv_freq.repeat_interleave(2, dim=-1)
+ return inv_freq
+
+
+def eager_attention_forward(
+ module: nn.Module,
+ query: torch.Tensor,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ attention_mask: torch.Tensor | None,
+ scaling: float,
+ dropout: float = 0.0,
+ **kwargs,
+):
+ attn_weights = torch.matmul(query, key.transpose(2, 3)) * scaling
+ if attention_mask is not None:
+ attn_weights = attn_weights + attention_mask
+
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query.dtype)
+ attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
+ attn_output = torch.matmul(attn_weights, value)
+ attn_output = attn_output.transpose(1, 2).contiguous()
+
+ return attn_output, attn_weights
+
+
+class EdgeTamVideoAttention(nn.Module):
+ """
+ EDGETAM_VIDEO's attention layer that allows for downscaling the size of the embedding after projection to queries, keys, and
+ values.
+ """
+
+ def __init__(self, config, downsample_rate=None):
+ super().__init__()
+ downsample_rate = config.attention_downsample_rate if downsample_rate is None else downsample_rate
+ self.config = config
+ self.hidden_size = config.hidden_size
+ self.internal_dim = config.hidden_size // downsample_rate
+ self.num_attention_heads = config.num_attention_heads
+ self.head_dim = self.internal_dim // config.num_attention_heads
+ self.scaling = self.head_dim**-0.5
+ self.is_causal = False
+
+ self.q_proj = nn.Linear(self.hidden_size, self.internal_dim)
+ self.k_proj = nn.Linear(self.hidden_size, self.internal_dim)
+ self.v_proj = nn.Linear(self.hidden_size, self.internal_dim)
+ self.o_proj = nn.Linear(self.internal_dim, self.hidden_size)
+
+ def forward(
+ self,
+ query: torch.Tensor,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ attention_similarity: torch.Tensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple[torch.Tensor, torch.Tensor]:
+ # Input projections
+ batch_size, point_batch_size = query.shape[:2]
+ new_shape = (batch_size * point_batch_size, -1, self.num_attention_heads, self.head_dim)
+
+ query = self.q_proj(query).view(*new_shape).transpose(1, 2)
+ key = self.k_proj(key).view(*new_shape).transpose(1, 2)
+ value = self.v_proj(value).view(*new_shape).transpose(1, 2)
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ if is_flash_attention_requested(self.config) and attention_similarity is not None:
+ # Target guided masks are represented as float masks and are incompatible with Flash Attention
+ # Fallback to SDPA for this call only so the rest of the model can still benefit from FA
+ attention_interface = ALL_ATTENTION_FUNCTIONS["sdpa"]
+ logger.warning_once(
+ "Falling back to SDPA for target-guided attention because "
+ "Flash Attention does not support additive bias masks."
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ query,
+ key,
+ value,
+ attention_mask=attention_similarity,
+ dropout=0.0,
+ scaling=self.scaling,
+ is_causal=self.is_causal,
+ **kwargs,
+ )
+
+ attn_output = attn_output.reshape(
+ batch_size, point_batch_size, -1, self.num_attention_heads * self.head_dim
+ ).contiguous()
+ attn_output = self.o_proj(attn_output)
+
+ return attn_output, attn_weights
+
+
+def rotate_pairwise(x):
+ """
+ pairwise rotation of the hidden dims of the input. Differerent from Llama Half-Tensor Rotation.
+
+ This is an optimized version of the following more explicit implementation:
+ ```python
+ x_rotated = torch.zeros_like(x, dtype=x.dtype, device=x.device)
+ x_rotated[..., ::2] = -x[..., 1::2]
+ x_rotated[..., 1::2] = x[..., ::2]
+ return x_rotated
+ ```
+ """
+ x = x.view(*x.shape[:-1], -1, 2)
+ x1, x2 = x.unbind(dim=-1)
+ x = torch.stack((-x2, x1), dim=-1)
+ return x.flatten(start_dim=-2)
+
+
+def apply_rotary_pos_emb_2d_self_attn(
+ q: torch.Tensor,
+ k: torch.Tensor,
+ cos: torch.Tensor,
+ sin: torch.Tensor,
+) -> tuple[torch.Tensor, torch.Tensor]:
+ """
+ Apply rotary position embedding to query and key tensors for self-attention.
+
+ Args:
+ q: Query tensor of shape (..., seq_len, head_dim)
+ k: Key tensor of shape (..., seq_len, head_dim)
+ cos: Cosine position embedding of shape (seq_len, head_dim)
+ sin: Sine position embedding of shape (seq_len, head_dim)
+
+ Returns:
+ Rotated (q, k) tensors
+ """
+ # Apply RoPE to queries
+ q_embed = q.float() # force upscale to float32 as in the original implementation
+ q_embed = (q_embed * cos) + (rotate_pairwise(q_embed) * sin)
+
+ # Apply RoPE to keys (same embeddings as queries for self-attention)
+ k_embed = k.float() # force upscale to float32 as in the original implementation
+ k_embed = (k_embed * cos) + (rotate_pairwise(k_embed) * sin)
+
+ return q_embed.type_as(q), k_embed.type_as(k)
+
+
+class EdgeTamVideoRoPESelfAttention(nn.Module):
+ """Self-attention with rotary position encoding."""
+
+ def __init__(self, config: EdgeTamVideoConfig):
+ super().__init__()
+ self.config = config
+ self.hidden_size = config.memory_attention_hidden_size
+ self.internal_dim = self.hidden_size // config.memory_attention_downsample_rate
+ self.num_attention_heads = config.memory_attention_num_attention_heads
+ self.head_dim = self.internal_dim // config.memory_attention_num_attention_heads
+ self.scaling = self.head_dim**-0.5
+ self.is_causal = False
+
+ self.q_proj = nn.Linear(self.hidden_size, self.internal_dim)
+ self.k_proj = nn.Linear(self.hidden_size, self.internal_dim)
+ self.v_proj = nn.Linear(self.hidden_size, self.internal_dim)
+ self.o_proj = nn.Linear(self.internal_dim, self.hidden_size)
+ self.dropout_p = config.memory_attention_rope_dropout
+
+ def forward(
+ self,
+ query: torch.Tensor,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor],
+ **kwargs: Unpack[FlashAttentionKwargs],
+ ) -> Tensor:
+ # Input projections
+ batch_size, point_batch_size = query.shape[:2]
+ new_shape = (batch_size * point_batch_size, -1, self.num_attention_heads, self.head_dim)
+
+ query = self.q_proj(query).view(*new_shape).transpose(1, 2)
+ key = self.k_proj(key).view(*new_shape).transpose(1, 2)
+ value = self.v_proj(value).view(*new_shape).transpose(1, 2)
+
+ cos, sin = position_embeddings
+ # Apply rotary position encoding for self-attention
+ query, key = apply_rotary_pos_emb_2d_self_attn(query, key, cos=cos, sin=sin)
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ query,
+ key,
+ value,
+ attention_mask=None,
+ dropout=0.0 if not self.training else self.dropout_p,
+ scaling=self.scaling,
+ is_causal=self.is_causal,
+ **kwargs,
+ )
+ attn_output = attn_output.reshape(
+ batch_size, point_batch_size, -1, self.num_attention_heads * self.head_dim
+ ).contiguous()
+ attn_output = self.o_proj(attn_output)
+ return attn_output, attn_weights
+
+
+def apply_rotary_pos_emb_2d_cross_attn(
+ q: torch.Tensor,
+ k: torch.Tensor,
+ cos: torch.Tensor,
+ sin: torch.Tensor,
+ cos_k: torch.Tensor,
+ sin_k: torch.Tensor,
+ num_k_exclude_rope: int = 0,
+ repeat_freqs_k: int = 1,
+) -> tuple[torch.Tensor, torch.Tensor]:
+ """
+ Apply rotary position embedding to query and key tensors for cross-attention.
+
+ Args:
+ q: Query tensor of shape (..., seq_len, head_dim)
+ k: Key tensor of shape (..., seq_len, head_dim)
+ cos: Cosine position embedding of shape (seq_len, head_dim)
+ sin: Sine position embedding of shape (seq_len, head_dim)
+ cos_k: Cosine position embedding for keys of shape (seq_len, head_dim)
+ sin_k: Sine position embedding for keys of shape (seq_len, head_dim)
+ num_k_exclude_rope: Number of tokens at end of k to exclude from RoPE (e.g., object pointer tokens)
+ repeat_freqs_k: Frequency repetition for keys in cross-attention (e.g., for spatial memory tokens)
+
+ Returns:
+ Rotated (q, k) tensors
+ """
+ # Apply RoPE to queries (always straightforward)
+ q_embed = q.float()
+ q_embed = (q_embed * cos) + (rotate_pairwise(q_embed) * sin)
+
+ # Split keys: RoPE tokens and excluded tokens (e.g., object pointers)
+ num_total_k_tokens = k.shape[-2]
+ k_for_rope = k[..., : num_total_k_tokens - num_k_exclude_rope, :]
+ k_excluded = k[..., num_total_k_tokens - num_k_exclude_rope :, :]
+
+ # Early return if no keys need RoPE
+ if k_for_rope.shape[-2] == 0:
+ return q_embed.type_as(q), k_excluded
+
+ batch_size, num_heads, k_seq_len, channels_per_head = k_for_rope.shape
+
+ # Handle temporal/spatial token structure for memory
+ # Keys have temporal + spatial structure, only spatial tokens get RoPE
+ tokens_per_group = k_seq_len // repeat_freqs_k
+ spatial_tokens = cos_k.shape[-2]
+ temporal_tokens = tokens_per_group - spatial_tokens
+
+ # Reshape and separate temporal/spatial tokens
+ k_grouped = k_for_rope.view(batch_size, num_heads, repeat_freqs_k, tokens_per_group, channels_per_head)
+ k_temporal = k_grouped[..., :temporal_tokens, :].reshape(batch_size, num_heads, -1, channels_per_head)
+ k_spatial = k_grouped[..., temporal_tokens:, :].reshape(batch_size, num_heads, -1, channels_per_head)
+
+ # Only apply RoPE to spatial tokens
+ k_rope_input = k_spatial
+
+ # Prepare position embeddings for repeated groups
+ if repeat_freqs_k > 1:
+ cos_k = cos_k.repeat(1, 1, repeat_freqs_k, 1)
+ sin_k = sin_k.repeat(1, 1, repeat_freqs_k, 1)
+
+ # Apply RoPE to spatial tokens
+ k_spatial_embed = k_rope_input.float()
+ k_spatial_embed = (k_spatial_embed * cos_k) + (rotate_pairwise(k_spatial_embed) * sin_k)
+
+ # Reconstruct: temporal + spatial tokens back to original structure
+ k_spatial_reshaped = k_spatial_embed.view(batch_size, num_heads, repeat_freqs_k, -1, channels_per_head)
+ k_temporal_reshaped = k_temporal.view(batch_size, num_heads, repeat_freqs_k, -1, channels_per_head)
+ k_final = torch.cat([k_temporal_reshaped, k_spatial_reshaped], dim=3)
+ k_final = k_final.view(batch_size, num_heads, k_seq_len, channels_per_head)
+
+ # Combine RoPE-processed keys with excluded tokens
+ k_embed = torch.cat([k_final.type_as(k), k_excluded], dim=-2)
+ return q_embed.type_as(q), k_embed
+
+
+class EdgeTamVideoRoPECrossAttention(nn.Module):
+ """Cross-attention with rotary position encoding."""
+
+ def __init__(self, config: EdgeTamVideoConfig, kv_in_dim: int):
+ super().__init__()
+ self.config = config
+ self.hidden_size = config.memory_attention_hidden_size
+ self.internal_dim = self.hidden_size // config.memory_attention_downsample_rate
+ self.num_attention_heads = config.memory_attention_num_attention_heads
+ self.head_dim = self.internal_dim // config.memory_attention_num_attention_heads
+ self.scaling = self.head_dim**-0.5
+ self.is_causal = False
+
+ self.kv_in_dim = kv_in_dim
+
+ self.q_proj = nn.Linear(self.hidden_size, self.internal_dim)
+ self.k_proj = nn.Linear(self.kv_in_dim, self.internal_dim)
+ self.v_proj = nn.Linear(self.kv_in_dim, self.internal_dim)
+ self.o_proj = nn.Linear(self.internal_dim, self.hidden_size)
+ self.dropout_p = config.memory_attention_rope_dropout
+
+ def forward(
+ self,
+ query: torch.Tensor,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor],
+ position_embeddings_k: tuple[torch.Tensor, torch.Tensor],
+ num_k_exclude_rope: int = 0,
+ rope_k_repeat: int = 0,
+ **kwargs: Unpack[FlashAttentionKwargs],
+ ) -> Tensor:
+ # Input projections
+ batch_size, point_batch_size = query.shape[:2]
+ new_shape = (batch_size * point_batch_size, -1, self.num_attention_heads, self.head_dim)
+
+ query = self.q_proj(query).view(*new_shape).transpose(1, 2)
+ key = self.k_proj(key).view(*new_shape).transpose(1, 2)
+ value = self.v_proj(value).view(*new_shape).transpose(1, 2)
+
+ cos, sin = position_embeddings
+ cos_k, sin_k = position_embeddings_k
+ # Apply rotary position encoding for cross-attention
+ query, key = apply_rotary_pos_emb_2d_cross_attn(
+ query,
+ key,
+ cos=cos,
+ sin=sin,
+ cos_k=cos_k,
+ sin_k=sin_k,
+ repeat_freqs_k=rope_k_repeat,
+ num_k_exclude_rope=num_k_exclude_rope,
+ )
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ query,
+ key,
+ value,
+ attention_mask=None,
+ dropout=0.0 if not self.training else self.dropout_p,
+ scaling=self.scaling,
+ is_causal=self.is_causal,
+ **kwargs,
+ )
+ attn_output = attn_output.reshape(
+ batch_size, point_batch_size, -1, self.num_attention_heads * self.head_dim
+ ).contiguous()
+ attn_output = self.o_proj(attn_output)
+ return attn_output, attn_weights
+
+
+class EdgeTamVideoTwoWayAttentionBlock(GradientCheckpointingLayer):
+ def __init__(self, config: EdgeTamVideoMaskDecoderConfig, skip_first_layer_pe: bool = False):
+ """
+ A transformer block with four layers:
+ (1) self-attention of sparse inputs (2) cross attention of sparse inputs -> dense inputs (3) mlp block on
+ sparse inputs (4) cross attention of dense inputs -> sparse inputs
+
+ Arguments:
+ config (`EdgeTamVideoMaskDecoderConfig`):
+ The configuration file used to instantiate the block
+ attention_downsample_rate (*optionalk*, int, defaults to 2):
+ The downsample ratio of the block used to reduce the inner dim of the attention.
+ skip_first_layer_pe (*optional*, bool, defaults to `False`):
+ Whether or not to skip the addition of the query_point_embedding on the first layer.
+ """
+ super().__init__()
+ self.self_attn = EdgeTamVideoAttention(config, downsample_rate=1)
+ self.layer_norm1 = nn.LayerNorm(config.hidden_size)
+
+ self.cross_attn_token_to_image = EdgeTamVideoAttention(config)
+ self.layer_norm2 = nn.LayerNorm(config.hidden_size)
+
+ self.mlp = EdgeTamVideoFeedForward(
+ config.hidden_size, config.mlp_dim, config.hidden_size, num_layers=config.num_hidden_layers
+ )
+ self.layer_norm3 = nn.LayerNorm(config.hidden_size)
+
+ self.layer_norm4 = nn.LayerNorm(config.hidden_size)
+ self.cross_attn_image_to_token = EdgeTamVideoAttention(config)
+
+ self.skip_first_layer_pe = skip_first_layer_pe
+
+ def forward(
+ self,
+ queries: Tensor,
+ keys: Tensor,
+ query_point_embedding: Tensor,
+ key_point_embedding: Tensor,
+ attention_similarity: Tensor,
+ **kwargs: Unpack[TransformersKwargs],
+ ):
+ # Self attention block
+ if self.skip_first_layer_pe:
+ queries, _ = self.self_attn(query=queries, key=queries, value=queries)
+ else:
+ query = queries + query_point_embedding
+ attn_out, _ = self.self_attn(query=query, key=query, value=queries)
+ queries = queries + attn_out
+ queries = self.layer_norm1(queries)
+
+ # Cross attention block, tokens attending to image embedding
+ query = queries + query_point_embedding
+ key = keys + key_point_embedding
+
+ attn_out, _ = self.cross_attn_token_to_image(
+ query=query, key=key, value=keys, attention_similarity=attention_similarity
+ )
+ queries = queries + attn_out
+
+ queries = self.layer_norm2(queries)
+
+ # MLP block
+ mlp_out = self.mlp(queries)
+ queries = queries + mlp_out
+ queries = self.layer_norm3(queries)
+
+ # Cross attention block, image embedding attending to tokens
+ query = queries + query_point_embedding
+ key = keys + key_point_embedding
+
+ attn_out, _ = self.cross_attn_image_to_token(query=key, key=query, value=queries)
+ keys = keys + attn_out
+
+ keys = self.layer_norm4(keys)
+ return queries, keys, attn_out
+
+
+# copied and adapted from original implementation, also practically equal to DetrSinePositionEmbedding
+class EdgeTamVideoPositionEmbeddingSine(nn.Module):
+ """
+ This is a more standard version of the position embedding, very similar to the one used by the Attention is all you
+ need paper, generalized to work on images.
+ """
+
+ def __init__(
+ self, num_pos_feats: int = 64, temperature: int = 10000, normalize: bool = False, scale: float | None = None
+ ):
+ super().__init__()
+ if scale is not None and normalize is False:
+ raise ValueError("normalize should be True if scale is passed")
+ self.num_pos_feats = num_pos_feats
+ self.temperature = temperature
+ self.normalize = normalize
+ self.scale = 2 * math.pi if scale is None else scale
+
+ @compile_compatible_method_lru_cache(maxsize=2)
+ def forward(
+ self,
+ shape: torch.Size,
+ device: torch.device | str,
+ dtype: torch.dtype,
+ mask: Tensor | None = None,
+ ) -> Tensor:
+ if mask is None:
+ mask = torch.zeros((shape[0], shape[2], shape[3]), device=device, dtype=torch.bool)
+ not_mask = (~mask).to(dtype)
+ y_embed = not_mask.cumsum(1)
+ x_embed = not_mask.cumsum(2)
+ if self.normalize:
+ eps = 1e-6
+ y_embed = y_embed / (y_embed[:, -1:, :] + eps) * self.scale
+ x_embed = x_embed / (x_embed[:, :, -1:] + eps) * self.scale
+
+ dim_t = torch.arange(self.num_pos_feats, dtype=torch.int64, device=device).to(dtype)
+ dim_t = self.temperature ** (2 * torch.div(dim_t, 2, rounding_mode="floor") / self.num_pos_feats)
+
+ pos_x = x_embed[:, :, :, None] / dim_t
+ pos_y = y_embed[:, :, :, None] / dim_t
+ pos_x = torch.stack((pos_x[:, :, :, 0::2].sin(), pos_x[:, :, :, 1::2].cos()), dim=4).flatten(3)
+ pos_y = torch.stack((pos_y[:, :, :, 0::2].sin(), pos_y[:, :, :, 1::2].cos()), dim=4).flatten(3)
+ pos = torch.cat((pos_y, pos_x), dim=3).permute(0, 3, 1, 2)
+ return pos
+
+
+class EdgeTamVideoMemoryFuser(nn.Module):
+ def __init__(self, config: EdgeTamVideoConfig):
+ super().__init__()
+ self.layers = nn.ModuleList(
+ [EdgeTamVideoMemoryFuserCXBlock(config) for _ in range(config.memory_fuser_num_layers)]
+ )
+
+ def forward(self, hidden_states):
+ # normally hidden_states: (N, C, H, W)
+ for layer in self.layers:
+ hidden_states = layer(hidden_states)
+ return hidden_states
+
+
+class EdgeTamVideoMaskDownSamplerLayer(nn.Module):
+ def __init__(self, config: EdgeTamVideoConfig, in_channels: int, out_channels: int):
+ super().__init__()
+ self.conv = nn.Conv2d(
+ in_channels,
+ out_channels,
+ kernel_size=config.mask_downsampler_kernel_size,
+ stride=config.mask_downsampler_stride,
+ padding=config.mask_downsampler_padding,
+ )
+ self.layer_norm = EdgeTamVideoLayerNorm(out_channels, eps=1e-6, data_format="channels_first")
+ self.activation = ACT2FN[config.mask_downsampler_hidden_act]
+
+ def forward(self, x):
+ return self.activation(self.layer_norm(self.conv(x)))
+
+
+class EdgeTamVideoMaskDownSampler(nn.Module):
+ """
+ Progressively downsample a mask by total_stride, each time by stride.
+ Note that LayerNorm is applied per *token*, like in ViT.
+
+ With each downsample (by a factor stride**2), channel capacity increases by the same factor.
+ In the end, we linearly project to embed_dim channels.
+ """
+
+ def __init__(self, config: EdgeTamVideoConfig):
+ super().__init__()
+
+ num_layers = int(math.log2(config.mask_downsampler_total_stride) // math.log2(config.mask_downsampler_stride))
+
+ self.layers = nn.ModuleList()
+ self.activation = ACT2FN[config.mask_downsampler_hidden_act]
+ mask_in_chans, mask_out_chans = 1, 1
+ for _ in range(num_layers):
+ mask_out_chans = mask_in_chans * (config.mask_downsampler_stride**2)
+ self.layers.append(EdgeTamVideoMaskDownSamplerLayer(config, mask_in_chans, mask_out_chans))
+ mask_in_chans = mask_out_chans
+
+ self.final_conv = nn.Conv2d(mask_out_chans, config.mask_downsampler_embed_dim, kernel_size=1)
+
+ def forward(self, x):
+ for layer in self.layers:
+ x = layer(x)
+ x = self.final_conv(x)
+ return x
+
+
+class EdgeTamVideoMemoryEncoder(nn.Module):
+ def __init__(self, config: EdgeTamVideoConfig):
+ super().__init__()
+
+ hidden_size = config.memory_encoder_hidden_size
+ output_channels = config.memory_encoder_output_channels
+ self.mask_downsampler = EdgeTamVideoMaskDownSampler(config)
+ self.feature_projection = nn.Conv2d(hidden_size, hidden_size, kernel_size=1)
+ self.memory_fuser = EdgeTamVideoMemoryFuser(config)
+ self.position_encoding = EdgeTamVideoPositionEmbeddingSine(num_pos_feats=output_channels // 2, normalize=True)
+ self.projection = nn.Conv2d(hidden_size, output_channels, kernel_size=1)
+
+ def forward(
+ self,
+ vision_features: torch.Tensor,
+ masks: torch.Tensor,
+ ) -> tuple[torch.Tensor, torch.Tensor]:
+ ## Process masks
+ masks = self.mask_downsampler(masks)
+ ## Fuse pixel_features and downsampled masks
+
+ vision_features = self.feature_projection(vision_features)
+ vision_features = vision_features + masks
+ vision_features = self.memory_fuser(vision_features)
+ vision_features = self.projection(vision_features)
+
+ vision_pos_enc = self.position_encoding(vision_features.shape, vision_features.device, vision_features.dtype)
+
+ return vision_features, vision_pos_enc
+
+
+class EdgeTamVideoFeedForward(nn.Module):
+ def __init__(
+ self,
+ input_dim: int,
+ hidden_dim: int,
+ output_dim: int,
+ num_layers: int,
+ activation: str = "relu",
+ sigmoid_output: bool = False,
+ ):
+ super().__init__()
+ self.num_layers = num_layers
+ self.activation = ACT2FN[activation]
+ self.proj_in = nn.Linear(input_dim, hidden_dim)
+ self.proj_out = nn.Linear(hidden_dim, output_dim)
+ self.layers = nn.ModuleList([nn.Linear(hidden_dim, hidden_dim) for _ in range(num_layers - 2)])
+ self.sigmoid_output = sigmoid_output
+
+ def forward(self, hidden_states):
+ hidden_states = self.proj_in(hidden_states)
+ hidden_states = self.activation(hidden_states)
+ for layer in self.layers:
+ hidden_states = self.activation(layer(hidden_states))
+
+ hidden_states = self.proj_out(hidden_states)
+ if self.sigmoid_output:
+ hidden_states = F.sigmoid(hidden_states)
+ return hidden_states
+
+
+class EdgeTamVideoPositionalEmbedding(nn.Module):
+ def __init__(self, config: EdgeTamVideoPromptEncoderConfig):
+ super().__init__()
+ self.scale = config.scale
+ positional_embedding = self.scale * torch.randn((2, config.hidden_size // 2))
+ self.register_buffer("positional_embedding", positional_embedding)
+
+ def forward(self, input_coords, input_shape=None):
+ """Positionally encode points that are normalized to [0,1]."""
+ coordinates = input_coords.clone()
+
+ if input_shape is not None:
+ coordinates[:, :, :, 0] = coordinates[:, :, :, 0] / input_shape[1]
+ coordinates[:, :, :, 1] = coordinates[:, :, :, 1] / input_shape[0]
+ coordinates.to(torch.float32)
+
+ # assuming coords are in [0, 1]^2 square and have d_1 x ... x d_n x 2 shape
+ coordinates = 2 * coordinates - 1
+ coordinates = coordinates.to(self.positional_embedding.dtype)
+ coordinates = coordinates @ self.positional_embedding
+ coordinates = 2 * np.pi * coordinates
+ # outputs d_1 x ... x d_n x channel shape
+ return torch.cat([torch.sin(coordinates), torch.cos(coordinates)], dim=-1)
+
+
+@auto_docstring
+class EdgeTamVideoPreTrainedModel(PreTrainedModel):
+ config_class = EdgeTamVideoConfig
+ base_model_prefix = "edgetam_video"
+ main_input_name = "pixel_values"
+ input_modalities = "video"
+ _supports_sdpa = True
+ _supports_flash_attn = True
+ _supports_attention_backend = True
+
+ @torch.no_grad()
+ def _init_weights(self, module):
+ super()._init_weights(module)
+ if isinstance(module, EdgeTamVideoModel):
+ if module.no_memory_positional_encoding is not None:
+ init.zeros_(module.no_memory_positional_encoding)
+ if module.memory_temporal_positional_encoding is not None:
+ init.zeros_(module.memory_temporal_positional_encoding)
+ if module.no_object_pointer is not None:
+ init.zeros_(module.no_object_pointer)
+ if module.occlusion_spatial_embedding_parameter is not None:
+ init.zeros_(module.occlusion_spatial_embedding_parameter)
+ if isinstance(module, EdgeTamVideoMemoryFuserCXBlock):
+ if module.scale is not None:
+ init.zeros_(module.scale)
+ elif isinstance(module, EdgeTamVideoVisionRotaryEmbedding):
+ inv_freq = module.create_inv_freq()
+ init.copy_(module.rope_embeddings_cos, inv_freq.cos())
+ init.copy_(module.rope_embeddings_sin, inv_freq.sin())
+ elif isinstance(module, EdgeTamVideoPositionalEmbedding):
+ init.normal_(module.positional_embedding, std=module.scale)
+ if isinstance(module, EdgeTamVideoVisionRotaryEmbedding):
+ inv_freq = module.create_inv_freq()
+ init.copy_(module.rope_embeddings_cos, inv_freq.cos())
+ init.copy_(module.rope_embeddings_sin, inv_freq.sin())
+
+
+class EdgeTamVideoInferenceCache:
+ """Cache for vision features and model constants."""
+
+ def __init__(
+ self,
+ inference_device: torch.device | str = "cpu",
+ inference_state_device: torch.device | str = "cpu",
+ max_vision_features_cache_size: int = 1,
+ ):
+ self.inference_device = inference_device
+ self.inference_state_device = inference_state_device
+ self.max_vision_features_cache_size = max_vision_features_cache_size
+
+ self._vision_features = {}
+
+ def cache_vision_features(self, frame_idx: int, features: dict):
+ """Cache vision features with automatic device management."""
+ cached = {}
+ if len(self._vision_features) >= self.max_vision_features_cache_size:
+ # remove the oldest frame
+ self._vision_features.pop(min(self._vision_features.keys()))
+
+ for key, value in features.items():
+ if isinstance(value, torch.Tensor):
+ cached[key] = value.to(self.inference_state_device, non_blocking=True)
+ elif isinstance(value, (list, tuple)) and value and isinstance(value[0], torch.Tensor):
+ cached[key] = [v.to(self.inference_state_device, non_blocking=True) for v in value]
+ else:
+ cached[key] = value
+ self._vision_features[frame_idx] = cached
+
+ def get_vision_features(self, frame_idx: int) -> dict | None:
+ """Get cached vision features, automatically moved to inference device."""
+ if frame_idx not in self._vision_features:
+ return None
+
+ cached = self._vision_features[frame_idx]
+ moved = {}
+ for key, value in cached.items():
+ if isinstance(value, torch.Tensor):
+ moved[key] = value.to(self.inference_device, non_blocking=True)
+ elif isinstance(value, (list, tuple)) and value and isinstance(value[0], torch.Tensor):
+ moved[key] = [v.to(self.inference_device, non_blocking=True) for v in value]
+ else:
+ moved[key] = value
+ return moved
+
+ def clear_all(self):
+ """Clear all cached data."""
+ self._vision_features.clear()
+
+
+class EdgeTamVideoInferenceSession:
+ r"""
+ Manages video inference session parameters, state and cache.
+
+ Args:
+ video (`torch.FloatTensor`, *optional*):
+ The video to process. No need to provide when streaming.
+ video_height (`int`, *optional*):
+ The height of the video.
+ video_width (`int`, *optional*):
+ The width of the video.
+ inference_device (`torch.device`, *optional*, defaults to `"cpu"`):
+ The device to use for inference.
+ inference_state_device (`torch.device`, *optional*, defaults to `"cpu"`):
+ The device to store the inference state on.
+ video_storage_device (`torch.device`, *optional*, defaults to `"cpu"`):
+ The device to store the video on.
+ dtype (`torch.dtype`, *optional*, defaults to `"float32"`):
+ The dtype to use for the video.
+ max_vision_features_cache_size (`int`, *optional*, defaults to 1):
+ The maximum number of vision features to cache.
+ """
+
+ def __init__(
+ self,
+ video: torch.FloatTensor | None = None,
+ video_height: int | None = None,
+ video_width: int | None = None,
+ inference_device: torch.device | str = "cpu",
+ inference_state_device: torch.device | str = "cpu",
+ video_storage_device: torch.device | str = "cpu",
+ dtype: torch.dtype | str = "float32",
+ max_vision_features_cache_size: int = 1,
+ ):
+ # store as a dictionary to avoid double memory allocation with torch.cat when adding new frames
+ self.processed_frames = (
+ dict(enumerate(video.to(video_storage_device, dtype=dtype))) if video is not None else None
+ )
+ self.video_height = video_height
+ self.video_width = video_width
+
+ self.inference_device = inference_device
+ self.inference_state_device = inference_state_device
+ self.video_storage_device = video_storage_device
+ self.dtype = dtype
+ self.max_vision_features_cache_size = max_vision_features_cache_size
+
+ # Cache for computed features
+ self.cache = EdgeTamVideoInferenceCache(
+ inference_device=self.inference_device,
+ inference_state_device=self.inference_state_device,
+ max_vision_features_cache_size=self.max_vision_features_cache_size,
+ )
+
+ # Persistent object tracking state
+ self._obj_id_to_idx = OrderedDict()
+ self._obj_idx_to_id = OrderedDict()
+ self.obj_ids = []
+
+ # Persistent user inputs
+ self.point_inputs_per_obj = {}
+ self.mask_inputs_per_obj = {}
+
+ # Persistent model outputs/history
+ self.output_dict_per_obj = {}
+ self.frames_tracked_per_obj = {}
+
+ # Session state flags
+ self.obj_with_new_inputs = []
+
+ @property
+ def num_frames(self) -> int | None:
+ return len(self.processed_frames) if self.processed_frames is not None else None
+
+ # Object management
+ def obj_id_to_idx(self, obj_id: int) -> int:
+ """Map object ID to index, creating new entry if needed."""
+ obj_idx = self._obj_id_to_idx.get(obj_id, None)
+ if obj_idx is not None:
+ return obj_idx
+
+ obj_idx = len(self._obj_id_to_idx)
+ self._obj_id_to_idx[obj_id] = obj_idx
+ self._obj_idx_to_id[obj_idx] = obj_id
+ self.obj_ids = list(self._obj_id_to_idx)
+
+ self.point_inputs_per_obj[obj_idx] = {}
+ self.mask_inputs_per_obj[obj_idx] = {}
+ self.output_dict_per_obj[obj_idx] = {
+ "cond_frame_outputs": {},
+ "non_cond_frame_outputs": {},
+ }
+ self.frames_tracked_per_obj[obj_idx] = {}
+
+ return obj_idx
+
+ # Video Inference specific functions
+ def obj_idx_to_id(self, obj_idx: int) -> int:
+ """Map model-side object index to client-side object id."""
+ return self._obj_idx_to_id[obj_idx]
+
+ def get_obj_num(self) -> int:
+ """Get the total number of unique object ids received so far in this session."""
+ return len(self._obj_idx_to_id)
+
+ # Input management with device handling
+ def add_point_inputs(self, obj_idx: int, frame_idx: int, inputs: dict):
+ """Add point inputs with automatic device placement."""
+ device_inputs = {}
+ for key, value in inputs.items():
+ if isinstance(value, torch.Tensor):
+ device_inputs[key] = value.to(self.inference_device, non_blocking=False)
+ else:
+ device_inputs[key] = value
+ self.point_inputs_per_obj[obj_idx][frame_idx] = device_inputs
+
+ def remove_point_inputs(self, obj_idx: int, frame_idx: int):
+ """Remove point inputs."""
+ self.point_inputs_per_obj[obj_idx].pop(frame_idx, None)
+
+ def add_mask_inputs(self, obj_idx: int, frame_idx: int, inputs: torch.Tensor):
+ """Add mask inputs with automatic device placement."""
+ self.mask_inputs_per_obj[obj_idx][frame_idx] = inputs.to(
+ self.inference_device, dtype=self.dtype, non_blocking=True
+ )
+
+ def remove_mask_inputs(self, obj_idx: int, frame_idx: int):
+ """Remove mask inputs."""
+ self.mask_inputs_per_obj[obj_idx].pop(frame_idx, None)
+
+ # Output management with smart device placement
+ def store_output(
+ self,
+ obj_idx: int,
+ frame_idx: int,
+ output_key: str | None = None,
+ output_value: torch.Tensor | dict | None = None,
+ is_conditioning_frame: bool = True,
+ ):
+ """
+ Store output with smart device management.
+ If output_key is None, the output is stored as a dictionary.
+
+ Args:
+ obj_idx (int): The index of the object.
+ frame_idx (int): The index of the frame.
+ output_key (Optional[str]): The key of the output. If None, the output is stored as a dictionary.
+ output_value (Optional[Union[torch.Tensor, dict]]): The value of the output.
+ is_conditioning_frame (bool): Whether the output is for a conditioning frame.
+ """
+ storage_key = "cond_frame_outputs" if is_conditioning_frame else "non_cond_frame_outputs"
+
+ if output_key is None and isinstance(output_value, dict):
+ self.output_dict_per_obj[obj_idx][storage_key][frame_idx] = {}
+ for key, value in output_value.items():
+ self.store_output(obj_idx, frame_idx, key, value, is_conditioning_frame)
+ return
+
+ # Device placement: small tensors stay on inference device, large ones go to inference state device
+ if output_key in ["object_pointer", "object_score_logits"]: # Small tensors
+ self.output_dict_per_obj[obj_idx][storage_key][frame_idx][output_key] = output_value
+ elif isinstance(output_value, torch.Tensor): # Large tensors like masks, features
+ self.output_dict_per_obj[obj_idx][storage_key][frame_idx][output_key] = output_value.to(
+ self.inference_state_device, non_blocking=True
+ )
+ else:
+ self.output_dict_per_obj[obj_idx][storage_key][frame_idx][output_key] = output_value
+
+ def get_output(
+ self,
+ obj_idx: int,
+ frame_idx: int,
+ output_key: str,
+ is_conditioning_frame: bool = True,
+ ):
+ """
+ Get output with smart device management.
+
+ Args:
+ obj_idx (int): The index of the object.
+ frame_idx (int): The index of the frame.
+ output_key (str): The key of the output.
+ is_conditioning_frame (bool): Whether the output is for a conditioning frame.
+ """
+ storage_key = "cond_frame_outputs" if is_conditioning_frame else "non_cond_frame_outputs"
+ out = self.output_dict_per_obj[obj_idx][storage_key].get(frame_idx, None)
+ # move to inference device if needed
+ if out is None:
+ return None
+ value = out[output_key]
+ if isinstance(value, torch.Tensor):
+ value = value.to(self.inference_device, non_blocking=True)
+ return value
+
+ # Video frame management
+ def add_new_frame(self, pixel_values: torch.Tensor, frame_idx: int | None = None) -> int:
+ """Add new frame with automatic device placement."""
+ pixel_values = pixel_values.to(self.video_storage_device, dtype=self.dtype, non_blocking=True)
+ if pixel_values.dim() == 4:
+ pixel_values = pixel_values.squeeze(0)
+
+ if frame_idx is None:
+ frame_idx = len(self.processed_frames) if self.processed_frames is not None else 0
+
+ if self.processed_frames is None:
+ self.processed_frames = {frame_idx: pixel_values}
+ else:
+ self.processed_frames[frame_idx] = pixel_values
+
+ return frame_idx
+
+ def get_frame(self, frame_idx: int) -> torch.Tensor:
+ """Get frame from video."""
+ return self.processed_frames[frame_idx].to(self.inference_device, non_blocking=True)
+
+ def reset_tracking_data(self):
+ """Reset tracking data but keep cache."""
+ self._obj_id_to_idx.clear()
+ self._obj_idx_to_id.clear()
+ self.obj_ids.clear()
+ self.point_inputs_per_obj.clear()
+ self.mask_inputs_per_obj.clear()
+ self.output_dict_per_obj.clear()
+ self.frames_tracked_per_obj.clear()
+ self.obj_with_new_inputs = []
+ # Note: cache and video data are preserved
+
+ def reset_inference_session(self):
+ """Reset tracking data and cache."""
+ self._obj_id_to_idx.clear()
+ self._obj_idx_to_id.clear()
+ self.obj_ids.clear()
+ self.point_inputs_per_obj.clear()
+ self.mask_inputs_per_obj.clear()
+ self.output_dict_per_obj.clear()
+ self.frames_tracked_per_obj.clear()
+ self.obj_with_new_inputs = []
+ self.cache.clear_all()
+
+
+class EdgeTamVideoMemoryAttentionMLP(nn.Module):
+ def __init__(self, config: EdgeTamVideoConfig):
+ super().__init__()
+ self.config = config
+ self.hidden_size = config.memory_attention_hidden_size
+ self.intermediate_size = config.memory_attention_mlp_hidden_size
+ self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size)
+ self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size)
+ self.dropout = nn.Dropout(config.memory_attention_dropout)
+ self.act_fn = ACT2FN[config.memory_attention_mlp_hidden_act]
+
+ def forward(self, x):
+ return self.down_proj(self.dropout(self.act_fn(self.up_proj(x))))
+
+
+class EdgeTamVideoMemoryAttentionLayer(nn.Module):
+ def __init__(self, config: EdgeTamVideoConfig):
+ super().__init__()
+ hidden_size = config.memory_attention_hidden_size
+ self.self_attn = EdgeTamVideoRoPESelfAttention(config)
+ self.cross_attn_image = EdgeTamVideoRoPECrossAttention(config, kv_in_dim=64)
+
+ # MLP module
+ self.mlp = EdgeTamVideoMemoryAttentionMLP(config)
+
+ self.layer_norm1 = nn.LayerNorm(hidden_size)
+ self.layer_norm2 = nn.LayerNorm(hidden_size)
+ self.layer_norm3 = nn.LayerNorm(hidden_size)
+ self.dropout1 = nn.Dropout(config.memory_attention_dropout)
+ self.dropout2 = nn.Dropout(config.memory_attention_dropout)
+ self.dropout3 = nn.Dropout(config.memory_attention_dropout)
+
+ def forward(
+ self,
+ queries: Tensor,
+ keys: Tensor,
+ key_point_embedding: Tensor,
+ rope_position_embeddings: tuple[Tensor, Tensor],
+ rope_position_embeddings_k: tuple[Tensor, Tensor] | None = None,
+ num_k_exclude_rope: int = 0,
+ rope_k_repeat: int = 0,
+ ) -> torch.Tensor:
+ # Self-Attention
+ query = self.layer_norm1(queries)
+ query, _ = self.self_attn(query=query, key=query, value=query, position_embeddings=rope_position_embeddings)
+ queries = queries + self.dropout1(query)
+
+ # Cross-Attention
+ query = self.layer_norm2(queries)
+ query, _ = self.cross_attn_image(
+ query=query,
+ key=keys + key_point_embedding,
+ value=keys,
+ position_embeddings=rope_position_embeddings,
+ position_embeddings_k=rope_position_embeddings_k,
+ num_k_exclude_rope=num_k_exclude_rope,
+ rope_k_repeat=rope_k_repeat,
+ )
+ queries = queries + self.dropout2(query)
+ # MLP
+ query = self.layer_norm3(queries)
+ query = self.mlp(query)
+ queries = queries + self.dropout3(query)
+ return queries
+
+
+class EdgeTamVideoMemoryAttention(nn.Module):
+ def __init__(self, config: EdgeTamVideoConfig):
+ super().__init__()
+ self.layers = nn.ModuleList(
+ [EdgeTamVideoMemoryAttentionLayer(config) for _ in range(config.memory_attention_num_layers)]
+ )
+ self.layer_norm = nn.LayerNorm(config.memory_attention_hidden_size)
+ self.rotary_emb = EdgeTamVideoVisionRotaryEmbedding(config=config)
+ self.rotary_emb_k = EdgeTamVideoVisionRotaryEmbedding(
+ config, end_x=config.memory_attention_rope_k_sizes[0], end_y=config.memory_attention_rope_k_sizes[1]
+ )
+
+ def forward(
+ self,
+ current_vision_features: torch.Tensor,
+ memory: torch.Tensor,
+ current_vision_position_embeddings: Tensor | None = None,
+ memory_posision_embeddings: Tensor | None = None,
+ num_object_pointer_tokens: int = 0,
+ num_spatial_memory_tokens: int = -1,
+ ):
+ """
+ Args:
+ current_vision_features (`torch.FloatTensor`):
+ The current vision features used for self-attention.
+ memory (`torch.FloatTensor`):
+ The memory features used for cross-attention.
+ current_vision_position_embeddings (`torch.FloatTensor`, *optional*):
+ The position embeddings for the current vision features.
+ memory_posision_embeddings (`torch.FloatTensor`, *optional*):
+ The position embeddings for the memory features.
+ num_object_pointer_tokens (`int`, *optional*, defaults to 0):
+ The number of object pointer tokens.
+ """
+ output = current_vision_features
+ if current_vision_position_embeddings is not None:
+ output = output + 0.1 * current_vision_position_embeddings
+
+ # Convert to batch first
+ output = output.transpose(0, 1)
+ memory = memory.transpose(0, 1).unsqueeze(1)
+ memory_posision_embeddings = memory_posision_embeddings.transpose(0, 1).unsqueeze(1)
+ rope_position_embeddings = self.rotary_emb()
+ rope_position_embeddings_k = self.rotary_emb_k()
+ for layer in self.layers:
+ output = layer(
+ queries=output.unsqueeze(1) if output.ndim == 3 else output,
+ keys=memory,
+ key_point_embedding=memory_posision_embeddings,
+ rope_position_embeddings=rope_position_embeddings,
+ rope_position_embeddings_k=rope_position_embeddings_k,
+ num_k_exclude_rope=num_object_pointer_tokens,
+ rope_k_repeat=num_spatial_memory_tokens,
+ )
+
+ normed_output = self.layer_norm(output)
+
+ # Convert back to seq first
+ normed_output = normed_output.transpose(0, 1)
+
+ return normed_output
+
+
+class EdgeTamVideoPerceiverMLP(nn.Module):
+ def __init__(self, config: EdgeTamVideoConfig):
+ super().__init__()
+ self.hidden_size = config.perceiver_resampler_hidden_size
+ self.intermediate_size = config.perceiver_resampler_mlp_intermediate_size
+
+ self.layer_norm = nn.LayerNorm(self.hidden_size)
+ self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
+ self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False)
+ self.act_fn = nn.GELU()
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ hidden_states = self.layer_norm(hidden_states)
+ hidden_states = self.down_proj(self.act_fn(self.up_proj(hidden_states)))
+ return hidden_states
+
+
+class EdgeTamVideoPerceiverAttention(nn.Module):
+ def __init__(self, config: EdgeTamVideoConfig):
+ super().__init__()
+ self.config = config
+ self.hidden_size = config.perceiver_resampler_hidden_size
+ self.num_attention_heads = config.perceiver_resampler_num_attention_heads
+ self.head_dim = config.perceiver_resampler_attention_head_dim
+ self.attention_dropout = config.perceiver_resampler_attention_dropout
+
+ self.inner_dim = self.head_dim * self.num_attention_heads
+ self.scaling = self.head_dim**-0.5
+ self.is_causal = False
+
+ self.q_proj = nn.Linear(self.hidden_size, self.inner_dim, bias=False)
+ self.k_proj = nn.Linear(self.hidden_size, self.inner_dim, bias=False)
+ self.v_proj = nn.Linear(self.hidden_size, self.inner_dim, bias=False)
+ self.o_proj = nn.Linear(self.inner_dim, self.hidden_size, bias=False)
+
+ def forward(
+ self,
+ query: torch.Tensor,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ positional_encoding: torch.Tensor | None = None,
+ **kwargs,
+ ) -> torch.Tensor:
+ # Project queries, keys, and values
+ query = self.q_proj(query)
+ key = self.k_proj(key)
+ value = self.v_proj(value)
+
+ # Reshape for multi-head attention
+ batch_size, seq_len_q = query.shape[:2]
+ query = query.view(batch_size, seq_len_q, self.num_attention_heads, self.head_dim).transpose(1, 2)
+ seq_len_kv = key.shape[1]
+ key = key.view(batch_size, seq_len_kv, self.num_attention_heads, self.head_dim).transpose(1, 2)
+ value = value.view(batch_size, seq_len_kv, self.num_attention_heads, self.head_dim).transpose(1, 2)
+
+ # Add positional encoding if provided
+ if positional_encoding is not None:
+ pos_encoding = positional_encoding.view(
+ batch_size, seq_len_kv, self.num_attention_heads, self.head_dim
+ ).transpose(1, 2)
+ key = key + pos_encoding
+ value = value + pos_encoding
+
+ # Apply attention
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, _ = attention_interface(
+ self,
+ query,
+ key,
+ value,
+ attention_mask=None,
+ dropout=0.0 if not self.training else self.attention_dropout,
+ scaling=self.scaling,
+ is_causal=self.is_causal,
+ **kwargs,
+ )
+
+ # Reshape output
+ attn_output = attn_output.transpose(1, 2).contiguous().view(batch_size, seq_len_q, self.inner_dim)
+ return self.o_proj(attn_output)
+
+
+class EdgeTamVideoPerceiverEncoderLayer(nn.Module):
+ def __init__(self, config: EdgeTamVideoConfig):
+ super().__init__()
+
+ self.cross_attention = EdgeTamVideoPerceiverAttention(config)
+ self.mlp = EdgeTamVideoPerceiverMLP(config)
+ self.dropout = nn.Dropout(config.perceiver_resampler_hidden_dropout)
+
+ self.self_attention = EdgeTamVideoPerceiverAttention(config)
+ self.self_mlp = EdgeTamVideoPerceiverMLP(config)
+
+ # Layer norms moved from attention classes to here
+ self.layer_norm_input = nn.LayerNorm(config.perceiver_resampler_hidden_size)
+ self.layer_norm_latents = nn.LayerNorm(config.perceiver_resampler_hidden_size)
+ self.layer_norm_self = nn.LayerNorm(config.perceiver_resampler_hidden_size)
+
+ def forward(
+ self,
+ latents: torch.Tensor,
+ input_features: torch.Tensor,
+ positional_encoding: torch.Tensor | None = None,
+ ) -> torch.Tensor:
+ # Cross attention with layer norms
+ normalized_latents = self.layer_norm_latents(latents)
+ normalized_input = self.layer_norm_input(input_features)
+ cross_attention_output = self.cross_attention(
+ query=normalized_latents,
+ key=normalized_input,
+ value=normalized_input,
+ positional_encoding=positional_encoding,
+ )
+ latents = latents + self.dropout(cross_attention_output)
+
+ mlp_output = self.mlp(latents)
+ latents = latents + mlp_output
+
+ # Self attention with layer norm
+ normalized_latents_self = self.layer_norm_self(latents)
+ self_attention_output = self.self_attention(
+ query=normalized_latents_self, key=normalized_latents_self, value=normalized_latents_self
+ )
+ latents = latents + self_attention_output
+
+ self_mlp_output = self.self_mlp(latents)
+ latents = latents + self_mlp_output
+
+ return latents
+
+
+def window_partition(hidden_state, window_size):
+ """
+ Partition into non-overlapping windows with padding if needed.
+
+ Args:
+ hidden_state (`torch.Tensor`):
+ Input tokens with [batch_size, height, width, num_channels].
+ window_size (`int`):
+ Window size.
+
+ Returns:
+ `tuple(torch.FloatTensor)` comprising various elements:
+ - windows: windows after partition with [batch_size * num_windows, window_size, window_size, num_channels].
+ - (padded_height, padded_width): padded height and width before partition
+ """
+ batch_size, height, width, num_channels = hidden_state.shape
+
+ pad_height = (window_size - height % window_size) % window_size
+ pad_width = (window_size - width % window_size) % window_size
+
+ # Noop in case pad_width == 0 and pad_height == 0.
+ hidden_state = nn.functional.pad(hidden_state, (0, 0, 0, pad_width, 0, pad_height))
+
+ padded_height, padded_width = height + pad_height, width + pad_width
+
+ hidden_state = hidden_state.view(
+ batch_size, padded_height // window_size, window_size, padded_width // window_size, window_size, num_channels
+ )
+ windows = hidden_state.permute(0, 1, 3, 2, 4, 5).contiguous().view(-1, window_size, window_size, num_channels)
+ return windows, (padded_height, padded_width)
+
+
+class EdgeTamVideoPerceiverResampler(nn.Module):
+ def __init__(self, config: EdgeTamVideoConfig):
+ super().__init__()
+ self.config = config
+ self.hidden_size = config.perceiver_resampler_hidden_size
+ self.num_latents_1d = config.perceiver_resampler_num_latents
+ self.num_latents_2d = config.perceiver_resampler_num_latents_2d
+ self.num_layers = config.perceiver_resampler_num_layers
+
+ if self.num_latents_1d > 0:
+ self.latents_1d = nn.Parameter(torch.randn(self.num_latents_1d, self.hidden_size))
+ if self.num_latents_2d > 0:
+ self.latents_2d = nn.Parameter(torch.randn(self.num_latents_2d, self.hidden_size))
+
+ self.positional_encoding = EdgeTamVideoPositionEmbeddingSine(
+ num_pos_feats=self.hidden_size // 2, normalize=True
+ )
+
+ self.layers = nn.ModuleList([EdgeTamVideoPerceiverEncoderLayer(config) for _ in range(self.num_layers)])
+
+ self.layer_norm = nn.LayerNorm(self.hidden_size)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ positional_encoding: torch.Tensor | None = None,
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
+ output_latents = []
+ output_positional_encodings = []
+
+ if self.num_latents_1d > 0:
+ latents_1d, pos_1d = self._forward_1d(hidden_states, positional_encoding)
+ output_latents.append(latents_1d)
+ output_positional_encodings.append(pos_1d)
+
+ if self.num_latents_2d > 0:
+ latents_2d, pos_2d = self._forward_2d(hidden_states)
+ output_latents.append(latents_2d)
+ output_positional_encodings.append(pos_2d)
+
+ combined_latents = torch.cat(output_latents, dim=1)
+
+ combined_positional_encoding = None
+ if positional_encoding is not None and output_positional_encodings:
+ combined_positional_encoding = torch.cat(output_positional_encodings, dim=1)
+
+ return combined_latents, combined_positional_encoding
+
+ def _forward_1d(
+ self,
+ hidden_states: torch.Tensor,
+ positional_encoding: torch.Tensor | None = None,
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
+ batch_size = hidden_states.shape[0]
+
+ latents = self.latents_1d.unsqueeze(0).expand(batch_size, -1, -1)
+ flattened_features = hidden_states.permute(0, 2, 3, 1).flatten(1, 2)
+
+ positional_features = None
+ if positional_encoding is not None:
+ positional_features = positional_encoding.permute(0, 2, 3, 1).flatten(1, 2)
+
+ for layer in self.layers:
+ latents = layer(latents, flattened_features, positional_features)
+
+ latents = self.layer_norm(latents)
+
+ output_positional_encoding = None
+ if positional_encoding is not None:
+ output_positional_encoding = torch.zeros_like(latents)
+
+ return latents, output_positional_encoding
+
+ def _forward_2d(self, hidden_states: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
+ batch_size, channels, height, width = hidden_states.shape
+
+ latents_2d = self.latents_2d.unsqueeze(0).expand(batch_size, -1, -1).view(-1, 1, channels)
+
+ num_windows_per_dim = int(math.sqrt(self.num_latents_2d))
+ window_size = height // num_windows_per_dim
+
+ windowed_input = hidden_states.permute(0, 2, 3, 1)
+ windowed_features, _ = window_partition(windowed_input, window_size)
+ windowed_features = windowed_features.flatten(1, 2)
+
+ for layer in self.layers:
+ latents_2d = layer(latents_2d, windowed_features, positional_encoding=None)
+
+ latents_2d = latents_2d.view(batch_size, num_windows_per_dim, num_windows_per_dim, channels).permute(
+ 0, 3, 1, 2
+ )
+
+ positional_encoding_2d = self.positional_encoding(latents_2d.shape, latents_2d.device, latents_2d.dtype).to(
+ dtype=hidden_states.dtype
+ )
+ positional_encoding_2d = positional_encoding_2d.permute(0, 2, 3, 1).flatten(1, 2)
+
+ latents_2d = latents_2d.permute(0, 2, 3, 1).flatten(1, 2)
+ latents_2d = self.layer_norm(latents_2d)
+
+ return latents_2d, positional_encoding_2d
+
+
+@dataclass
+@auto_docstring(custom_intro="Base class for the EdgeTamVideo model's output.")
+class EdgeTamVideoImageSegmentationOutput(ModelOutput):
+ r"""
+ iou_scores (`torch.FloatTensor` of shape `(batch_size, point_batch_size, num_masks)`):
+ The Intersection over Union (IoU) scores of the predicted masks.
+ pred_masks (`torch.FloatTensor` of shape `(batch_size, point_batch_size, num_masks, height, width)`):
+ The predicted low-resolution masks. This is an alias for `low_res_masks`. These masks need to be post-processed
+ by the processor to be brought to the original image size.
+ object_score_logits (`torch.FloatTensor` of shape `(batch_size, point_batch_size, 1)`):
+ Logits for the object score, indicating if an object is present.
+ image_embeddings (`tuple(torch.FloatTensor)`):
+ The features from the FPN, which are used by the mask decoder. This is a tuple of `torch.FloatTensor` where each
+ tensor has shape `(batch_size, channels, height, width)`.
+ vision_hidden_states (`tuple(torch.FloatTensor)`, *optional*, returned when `output_hidden_states=True`):
+ Tuple of `torch.FloatTensor` (one for the output of each stage) of shape `(batch_size, height, width, hidden_size)`.
+ Hidden-states of the vision model at the output of each stage.
+ vision_attentions (`tuple(torch.FloatTensor)`, *optional*, returned when `output_attentions=True`):
+ Tuple of `torch.FloatTensor` (one for each layer) of shape `(batch_size, num_heads, sequence_length, sequence_length)`.
+ Attentions weights of the vision model.
+ mask_decoder_attentions (`tuple(torch.FloatTensor)`, *optional*, returned when `output_attentions=True`):
+ Tuple of `torch.FloatTensor` (one for each layer) of shape `(batch_size, num_heads, sequence_length, sequence_length)`.
+ Attentions weights of the mask decoder.
+ high_res_masks (`torch.FloatTensor` of shape `(batch_size, point_batch_size, num_masks, image_size, image_size)`, *optional*):
+ The predicted masks, upscaled to the original image size. Only used for EdgeTamVideoModel.
+ object_pointer (`torch.FloatTensor` of shape `(batch_size, point_batch_size, hidden_size)`, *optional*):
+ A tensor representing the object pointer, used for tracking in videos. Only used for EdgeTamVideoModel.
+ """
+
+ iou_scores: torch.FloatTensor | None = None
+ pred_masks: torch.FloatTensor | None = None
+ object_score_logits: torch.FloatTensor | None = None
+ image_embeddings: tuple[torch.FloatTensor, ...] = None
+ vision_hidden_states: tuple[torch.FloatTensor, ...] | None = None
+ vision_attentions: tuple[torch.FloatTensor, ...] | None = None
+ mask_decoder_attentions: tuple[torch.FloatTensor, ...] | None = None
+
+ high_res_masks: torch.FloatTensor | None = None
+ object_pointer: torch.FloatTensor | None = None
+
+
+@dataclass
+@auto_docstring(custom_intro="Base class for the Sam2 model's output.")
+class EdgeTamVideoSegmentationOutput(ModelOutput):
+ r"""
+ object_ids (`list[int]`, *optional*):
+ List of object IDs being tracked in the current frame.
+ pred_masks (`torch.FloatTensor` of shape `(batch_size, num_masks, height, width)`):
+ The predicted masks stored at the model's resolution.
+ object_score_logits (`torch.FloatTensor` of shape `(batch_size,)`, *optional*):
+ Logits for the object scores, indicating if objects are present.
+ frame_idx (`int`):
+ The frame index of the video.
+ """
+
+ object_ids: list[int] | None = None
+ pred_masks: torch.FloatTensor | None = None
+ object_score_logits: torch.FloatTensor | None = None
+ frame_idx: int | None = None
+
+
+class EdgeTamVideoMaskEmbedding(nn.Module):
+ def __init__(self, config: EdgeTamVideoPromptEncoderConfig):
+ super().__init__()
+ self.mask_input_channels = config.mask_input_channels // 4
+ self.activation = ACT2FN[config.hidden_act]
+ self.conv1 = nn.Conv2d(1, self.mask_input_channels, kernel_size=2, stride=2)
+ self.conv2 = nn.Conv2d(self.mask_input_channels, config.mask_input_channels, kernel_size=2, stride=2)
+ self.conv3 = nn.Conv2d(config.mask_input_channels, config.hidden_size, kernel_size=1)
+ self.layer_norm1 = EdgeTamVideoLayerNorm(
+ self.mask_input_channels, eps=config.layer_norm_eps, data_format="channels_first"
+ )
+ self.layer_norm2 = EdgeTamVideoLayerNorm(
+ self.mask_input_channels * 4, eps=config.layer_norm_eps, data_format="channels_first"
+ )
+
+ def forward(self, masks):
+ hidden_states = self.conv1(masks)
+ hidden_states = self.layer_norm1(hidden_states)
+ hidden_states = self.activation(hidden_states)
+
+ hidden_states = self.conv2(hidden_states)
+ hidden_states = self.layer_norm2(hidden_states)
+ hidden_states = self.activation(hidden_states)
+ dense_embeddings = self.conv3(hidden_states)
+ return dense_embeddings
+
+
+class EdgeTamVideoPromptEncoder(nn.Module):
+ def __init__(self, config: EdgeTamVideoPromptEncoderConfig):
+ super().__init__()
+ self.shared_embedding = EdgeTamVideoPositionalEmbedding(config)
+ self.mask_embed = EdgeTamVideoMaskEmbedding(config)
+ self.no_mask_embed = nn.Embedding(1, config.hidden_size)
+
+ self.image_embedding_size = (config.image_size // config.patch_size, config.image_size // config.patch_size)
+ self.mask_input_size = (4 * config.image_size // config.patch_size, 4 * config.image_size // config.patch_size)
+ self.input_image_size = config.image_size
+
+ self.point_embed = nn.Embedding(config.num_point_embeddings, config.hidden_size)
+ self.hidden_size = config.hidden_size
+ self.not_a_point_embed = nn.Embedding(1, config.hidden_size)
+
+ def _embed_points(self, points: torch.Tensor, labels: torch.Tensor, pad: bool) -> torch.Tensor:
+ """Embeds point prompts."""
+ points = points + 0.5 # Shift to center of pixel
+ if pad:
+ points = torch.nn.functional.pad(points, (0, 0, 0, 1), mode="constant", value=0)
+ labels = torch.nn.functional.pad(labels, (0, 1), mode="constant", value=-1)
+ input_shape = (self.input_image_size, self.input_image_size)
+ point_embedding = self.shared_embedding(points, input_shape)
+
+ # torch.where and expanding the labels tensor is required by the ONNX export
+ point_embedding = torch.where(labels[..., None] == -1, self.not_a_point_embed.weight, point_embedding)
+
+ # This is required for the ONNX export. The dtype, device need to be explicitly
+ # specified as otherwise torch.onnx.export interprets as double
+ point_embedding = torch.where(
+ labels[..., None] != -10,
+ point_embedding,
+ torch.zeros_like(point_embedding),
+ )
+
+ # Add point embeddings for labels >= 0
+ point_embedding = point_embedding + self.point_embed(labels.clamp(min=0)) * (labels >= 0).unsqueeze(-1)
+
+ return point_embedding
+
+ def _embed_boxes(self, boxes: torch.Tensor) -> torch.Tensor:
+ """Embeds box prompts."""
+ boxes = boxes + 0.5 # Shift to center of pixel
+ coords = boxes.view(*boxes.shape[:2], 2, 2)
+ # add padding point for consistency with the original implementation
+ coords = torch.nn.functional.pad(coords, (0, 0, 0, 1), mode="constant", value=0)
+ corner_embedding = self.shared_embedding(coords, (self.input_image_size, self.input_image_size))
+ corner_embedding[:, :, 0, :] += self.point_embed.weight[2]
+ corner_embedding[:, :, 1, :] += self.point_embed.weight[3]
+ corner_embedding[:, :, 2, :] = self.not_a_point_embed.weight.expand_as(corner_embedding[:, :, 2, :])
+ return corner_embedding
+
+ def forward(
+ self,
+ input_points: tuple[torch.Tensor, torch.Tensor] | None,
+ input_labels: torch.Tensor | None,
+ input_boxes: torch.Tensor | None,
+ input_masks: torch.Tensor | None,
+ ) -> tuple[torch.Tensor, torch.Tensor]:
+ """
+ Embeds different types of prompts, returning both sparse and dense embeddings.
+
+ Args:
+ points (`torch.Tensor`, *optional*):
+ point coordinates and labels to embed.
+ boxes (`torch.Tensor`, *optional*):
+ boxes to embed
+ masks (`torch.Tensor`, *optional*):
+ masks to embed
+ """
+ sparse_embeddings = None
+ batch_size = 1
+ if input_points is not None:
+ batch_size = input_points.shape[0]
+ if input_labels is None:
+ raise ValueError("If points are provided, labels must also be provided.")
+ point_embeddings = self._embed_points(input_points, input_labels, pad=(input_boxes is None))
+ sparse_embeddings = point_embeddings
+ if input_boxes is not None:
+ batch_size = input_boxes.shape[0]
+ box_embeddings = self._embed_boxes(input_boxes)
+ if sparse_embeddings is None:
+ sparse_embeddings = box_embeddings
+ else:
+ sparse_embeddings = torch.cat([sparse_embeddings, box_embeddings], dim=2)
+ if input_masks is not None:
+ dense_embeddings = self.mask_embed(input_masks)
+ else:
+ dense_embeddings = self.no_mask_embed.weight.reshape(1, -1, 1, 1).expand(
+ batch_size, -1, self.image_embedding_size[0], self.image_embedding_size[1]
+ )
+
+ return sparse_embeddings, dense_embeddings
+
+
+class EdgeTamVideoTwoWayTransformer(nn.Module):
+ def __init__(self, config: EdgeTamVideoMaskDecoderConfig):
+ super().__init__()
+ self.config = config
+
+ self.num_hidden_layers = config.num_hidden_layers
+ self.layers = nn.ModuleList()
+
+ for i in range(self.num_hidden_layers):
+ self.layers.append(EdgeTamVideoTwoWayAttentionBlock(config, skip_first_layer_pe=(i == 0)))
+
+ self.final_attn_token_to_image = EdgeTamVideoAttention(config)
+ self.layer_norm_final_attn = nn.LayerNorm(config.hidden_size)
+
+ def forward(
+ self,
+ point_embeddings: Tensor,
+ image_embeddings: Tensor,
+ image_positional_embeddings: Tensor,
+ attention_similarity: Tensor,
+ target_embedding=None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | BaseModelOutput:
+ if image_embeddings is None:
+ raise ValueError("You have to specify an image_embedding")
+
+ image_embeddings = image_embeddings.flatten(2).permute(0, 2, 1).unsqueeze(1)
+ image_positional_embeddings = image_positional_embeddings.flatten(2).permute(0, 2, 1).unsqueeze(1)
+
+ # Prepare queries
+ queries = point_embeddings
+ keys = image_embeddings
+
+ # Apply transformer blocks and final layernorm
+ for layer in self.layers:
+ if target_embedding is not None:
+ queries += target_embedding
+
+ queries, keys, _ = layer(
+ queries=queries,
+ keys=keys,
+ query_point_embedding=point_embeddings,
+ key_point_embedding=image_positional_embeddings,
+ attention_similarity=attention_similarity,
+ **kwargs,
+ )
+ # Apply the final attention layer from the points to the image
+ query = queries + point_embeddings
+ key = keys + image_positional_embeddings
+
+ attn_out, _ = self.final_attn_token_to_image(query=query, key=key, value=keys)
+
+ queries = queries + attn_out
+ queries = self.layer_norm_final_attn(queries)
+ return queries, keys
+
+
+class EdgeTamVideoMaskDecoder(nn.Module):
+ def __init__(self, config: EdgeTamVideoMaskDecoderConfig):
+ super().__init__()
+ self.config = config
+ self.hidden_size = config.hidden_size
+
+ self.num_multimask_outputs = config.num_multimask_outputs
+ self.num_mask_tokens = config.num_multimask_outputs + 1
+
+ self.iou_token = nn.Embedding(1, self.hidden_size)
+ self.mask_tokens = nn.Embedding(self.num_mask_tokens, self.hidden_size)
+
+ self.transformer = EdgeTamVideoTwoWayTransformer(config)
+
+ # should we create a new class for this?
+ self.upscale_conv1 = nn.ConvTranspose2d(self.hidden_size, self.hidden_size // 4, kernel_size=2, stride=2)
+ self.upscale_conv2 = nn.ConvTranspose2d(self.hidden_size // 4, self.hidden_size // 8, kernel_size=2, stride=2)
+ self.upscale_layer_norm = EdgeTamVideoLayerNorm(self.hidden_size // 4, data_format="channels_first")
+ self.activation = nn.GELU()
+
+ mlps_list = []
+ for _ in range(self.num_mask_tokens):
+ mlps_list += [EdgeTamVideoFeedForward(self.hidden_size, self.hidden_size, self.hidden_size // 8, 3)]
+ self.output_hypernetworks_mlps = nn.ModuleList(mlps_list)
+ self.iou_prediction_head = EdgeTamVideoFeedForward(
+ self.hidden_size,
+ config.iou_head_hidden_dim,
+ self.num_mask_tokens,
+ config.iou_head_depth,
+ sigmoid_output=True,
+ )
+
+ self.conv_s0 = nn.Conv2d(config.hidden_size, config.hidden_size // 8, kernel_size=1, stride=1)
+ self.conv_s1 = nn.Conv2d(config.hidden_size, config.hidden_size // 4, kernel_size=1, stride=1)
+
+ self.obj_score_token = nn.Embedding(1, self.hidden_size)
+ self.pred_obj_score_head = EdgeTamVideoFeedForward(self.hidden_size, self.hidden_size, 1, 3)
+
+ self.dynamic_multimask_via_stability = config.dynamic_multimask_via_stability
+ self.dynamic_multimask_stability_delta = config.dynamic_multimask_stability_delta
+ self.dynamic_multimask_stability_thresh = config.dynamic_multimask_stability_thresh
+
+ def forward(
+ self,
+ image_embeddings: torch.Tensor,
+ image_positional_embeddings: torch.Tensor,
+ sparse_prompt_embeddings: torch.Tensor,
+ dense_prompt_embeddings: torch.Tensor,
+ multimask_output: bool,
+ high_resolution_features: list[torch.Tensor],
+ attention_similarity: torch.Tensor | None = None,
+ target_embedding: torch.Tensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
+ """
+ Predict masks given image and prompt embeddings.
+
+ Args:
+ image_embeddings (`torch.Tensor`):
+ The embeddings from the image encoder.
+ image_positional_embeddings (`torch.Tensor`):
+ Positional encoding with the shape of image_embeddings.
+ sparse_prompt_embeddings (`torch.Tensor`):
+ The embeddings of the points and boxes.
+ dense_prompt_embeddings (`torch.Tensor`):
+ The embeddings of the mask inputs.
+ multimask_output (`bool`):
+ Whether to return multiple masks or a single mask.
+ high_resolution_features (`list[torch.Tensor]`, *optional*):
+ The high-resolution features from the vision encoder.
+ attention_similarity (`torch.Tensor`, *optional*):
+ The attention similarity tensor.
+ target_embedding (`torch.Tensor`, *optional*):
+ The target embedding.
+ """
+ batch_size, num_channels, height, width = image_embeddings.shape
+ point_batch_size = sparse_prompt_embeddings.shape[1]
+ # Concatenate output tokens
+ output_tokens = torch.cat(
+ [
+ self.obj_score_token.weight,
+ self.iou_token.weight,
+ self.mask_tokens.weight,
+ ],
+ dim=0,
+ )
+ output_tokens = output_tokens.repeat(batch_size, point_batch_size, 1, 1)
+
+ if sparse_prompt_embeddings.shape[0] != 0:
+ tokens = torch.cat((output_tokens, sparse_prompt_embeddings), dim=2)
+ else:
+ tokens = output_tokens
+ point_embeddings = tokens.to(self.iou_token.weight.dtype)
+
+ # Expand per-image data in batch direction to be per-mask
+ image_embeddings = image_embeddings + dense_prompt_embeddings
+ image_embeddings = image_embeddings.repeat_interleave(point_batch_size, dim=0)
+ image_positional_embeddings = image_positional_embeddings.repeat_interleave(point_batch_size, 0)
+ # Run the transformer
+ point_embeddings, image_embeddings = self.transformer(
+ point_embeddings=point_embeddings,
+ image_embeddings=image_embeddings,
+ image_positional_embeddings=image_positional_embeddings,
+ attention_similarity=attention_similarity,
+ target_embedding=target_embedding,
+ **kwargs,
+ )
+ iou_token_out = point_embeddings[:, :, 1, :]
+ mask_tokens_out = point_embeddings[:, :, 2 : (2 + self.num_mask_tokens), :]
+
+ # Upscale mask embeddings and predict masks using the mask tokens
+ image_embeddings = image_embeddings.transpose(2, 3).view(
+ batch_size * point_batch_size, num_channels, height, width
+ )
+
+ feat_s0, feat_s1 = high_resolution_features
+ feat_s0 = feat_s0.repeat_interleave(point_batch_size, dim=0)
+ feat_s1 = feat_s1.repeat_interleave(point_batch_size, dim=0)
+ upscaled_embedding = self.upscale_conv1(image_embeddings) + feat_s1
+ upscaled_embedding = self.activation(self.upscale_layer_norm(upscaled_embedding))
+ upscaled_embedding = self.activation(self.upscale_conv2(upscaled_embedding) + feat_s0)
+
+ hyper_in_list: list[torch.Tensor] = []
+ for i in range(self.num_mask_tokens):
+ current_mlp = self.output_hypernetworks_mlps[i]
+ hyper_in_list += [current_mlp(mask_tokens_out[:, :, i, :])]
+ hyper_in = torch.stack(hyper_in_list, dim=2)
+
+ _, num_channels, height, width = upscaled_embedding.shape
+ upscaled_embedding = upscaled_embedding.view(batch_size, point_batch_size, num_channels, height * width)
+ masks = (hyper_in @ upscaled_embedding).view(batch_size, point_batch_size, -1, height, width)
+
+ # Generate mask quality predictions
+ iou_pred = self.iou_prediction_head(iou_token_out)
+ object_score_logits = self.pred_obj_score_head(point_embeddings[:, :, 0, :])
+
+ # Select the correct mask or masks for output
+ if multimask_output:
+ mask_slice = slice(1, None)
+ masks = masks[:, :, mask_slice, :, :]
+ iou_pred = iou_pred[:, :, mask_slice]
+ elif self.dynamic_multimask_via_stability and not self.training:
+ mask_slice = slice(0, 1)
+ masks, iou_pred = self._dynamic_multimask_via_stability(masks, iou_pred)
+ else:
+ mask_slice = slice(0, 1)
+ masks = masks[:, :, mask_slice, :, :]
+ iou_pred = iou_pred[:, :, mask_slice]
+
+ sam_tokens_out = mask_tokens_out[:, :, mask_slice] # [b, 3, c] shape
+
+ return masks, iou_pred, sam_tokens_out, object_score_logits
+
+ def _get_stability_scores(self, mask_logits):
+ """
+ Compute stability scores of the mask logits based on the IoU between upper and
+ lower thresholds.
+ """
+ mask_logits = mask_logits.flatten(-2)
+ stability_delta = self.dynamic_multimask_stability_delta
+ area_i = torch.sum(mask_logits > stability_delta, dim=-1).float()
+ area_u = torch.sum(mask_logits > -stability_delta, dim=-1).float()
+ stability_scores = torch.where(area_u > 0, area_i / area_u, 1.0)
+ return stability_scores
+
+ def _dynamic_multimask_via_stability(self, all_mask_logits, all_iou_scores):
+ """
+ When outputting a single mask, if the stability score from the current single-mask
+ output (based on output token 0) falls below a threshold, we instead select from
+ multi-mask outputs (based on output token 1~3) the mask with the highest predicted
+ IoU score. This is intended to ensure a valid mask for both clicking and tracking.
+ """
+ # The best mask from multimask output tokens (1~3)
+ multimask_logits = all_mask_logits[:, :, 1:, :, :]
+ multimask_iou_scores = all_iou_scores[:, :, 1:]
+ best_scores_inds = torch.argmax(multimask_iou_scores, dim=-1) # [B, P]
+ best_scores_inds_expanded = best_scores_inds.unsqueeze(-1).unsqueeze(-1).unsqueeze(-1)
+ best_scores_inds_expanded = best_scores_inds_expanded.expand(
+ -1, -1, 1, multimask_logits.size(-2), multimask_logits.size(-1)
+ )
+ best_multimask_logits = torch.gather(multimask_logits, 2, best_scores_inds_expanded) # [B, P, 1, H, W]
+ best_multimask_iou_scores = torch.gather(multimask_iou_scores, 2, best_scores_inds.unsqueeze(-1)) # [B, P, 1]
+
+ # The mask from singlemask output token 0 and its stability score
+ singlemask_logits = all_mask_logits[:, :, 0:1, :, :]
+ singlemask_iou_scores = all_iou_scores[:, :, 0:1]
+ stability_scores = self._get_stability_scores(singlemask_logits)
+ is_stable = stability_scores >= self.dynamic_multimask_stability_thresh
+
+ # Dynamically fall back to best multimask output upon low stability scores.
+ mask_logits_out = torch.where(
+ is_stable[..., None, None].expand_as(singlemask_logits),
+ singlemask_logits,
+ best_multimask_logits,
+ )
+ iou_scores_out = torch.where(
+ is_stable.expand_as(singlemask_iou_scores),
+ singlemask_iou_scores,
+ best_multimask_iou_scores,
+ )
+ return mask_logits_out, iou_scores_out
+
+
+# a large negative value as a placeholder score for missing objects
+NO_OBJ_SCORE = -1024.0
+
+
+def get_1d_sine_pe(pos_inds, dim, temperature=10000):
+ """
+ Get 1D sine positional embedding as in the original Transformer paper.
+ """
+ pe_dim = dim // 2
+ dim_t = torch.arange(pe_dim, dtype=torch.float32, device=pos_inds.device)
+ dim_t = temperature ** (2 * (dim_t // 2) / pe_dim)
+
+ pos_embed = pos_inds.unsqueeze(-1) / dim_t
+ pos_embed = torch.cat([pos_embed.sin(), pos_embed.cos()], dim=-1)
+ return pos_embed
+
+
+@auto_docstring
+class EdgeTamVideoModel(EdgeTamVideoPreTrainedModel):
+ input_modalities = ("video", "text")
+ _can_record_outputs = {"mask_decoder_attentions": OutputRecorder(EdgeTamVideoTwoWayAttentionBlock, index=2)}
+ _tied_weights_keys = {}
+ _keys_to_ignore_on_load_unexpected = []
+
+ def __init__(self, config: EdgeTamVideoConfig):
+ super().__init__(config)
+ self.shared_image_embedding = EdgeTamVideoPositionalEmbedding(config.prompt_encoder_config)
+ self.vision_encoder = AutoModel.from_config(config.vision_config)
+ self.prompt_encoder = EdgeTamVideoPromptEncoder(config.prompt_encoder_config)
+ # The module using it is not a PreTrainedModel subclass so we need this
+ config.mask_decoder_config._attn_implementation = config._attn_implementation
+ self.mask_decoder = EdgeTamVideoMaskDecoder(config.mask_decoder_config)
+
+ self.num_feature_levels = config.vision_config.num_feature_levels
+ self.backbone_feature_sizes = config.vision_config.backbone_feature_sizes
+ # a single token to indicate no memory embedding from previous frames
+ self.hidden_dim = config.vision_config.fpn_hidden_size
+ self.no_memory_embedding = torch.nn.Parameter(torch.zeros(1, 1, self.hidden_dim))
+ self.config = config
+ # For video sequence inference
+ self.image_size = config.image_size
+ self.memory_attention = EdgeTamVideoMemoryAttention(config)
+ self.memory_encoder = EdgeTamVideoMemoryEncoder(config)
+ self.no_memory_positional_encoding = torch.nn.Parameter(
+ torch.zeros(1, 1, config.vision_config.fpn_hidden_size)
+ )
+ self.mem_dim = config.memory_encoder_output_channels
+ self.num_maskmem = config.num_maskmem # Number of memories accessible
+ # Temporal encoding of the memories
+ self.memory_temporal_positional_encoding = torch.nn.Parameter(
+ torch.zeros(self.num_maskmem, 1, 1, self.mem_dim)
+ )
+
+ self.no_object_pointer = torch.nn.Parameter(torch.zeros(1, self.hidden_dim))
+ # A conv layer to downsample the mask prompt to stride 4 (the same stride as
+ # low-res SAM mask logits) and to change its scales from 0~1 to SAM logit scale,
+ # so that it can be fed into the SAM mask decoder to generate a pointer.
+ self.mask_downsample = torch.nn.Conv2d(1, 1, kernel_size=4, stride=4)
+ # a feedforward layer on SAM output tokens to turn them into object pointers
+ self.object_pointer_proj = EdgeTamVideoFeedForward(self.hidden_dim, self.hidden_dim, self.hidden_dim, 3)
+
+ if self.config.enable_temporal_pos_encoding_for_object_pointers:
+ # a linear projection on temporal positional encoding in object pointers to
+ # avoid potential interference with spatial positional encoding
+ self.temporal_positional_encoding_projection_layer = torch.nn.Linear(self.hidden_dim, self.mem_dim)
+ else:
+ self.temporal_positional_encoding_projection_layer = torch.nn.Identity()
+
+ self.occlusion_spatial_embedding_parameter = None # compatibility with Sam2
+ if config.enable_occlusion_spatial_embedding:
+ self.occlusion_spatial_embedding_parameter = torch.nn.Parameter(torch.zeros(1, self.mem_dim))
+ self.spatial_perceiver = EdgeTamVideoPerceiverResampler(config)
+
+ self.post_init()
+
+ def get_input_embeddings(self):
+ return self.vision_encoder.get_input_embeddings()
+
+ def get_image_wide_positional_embeddings(self) -> torch.Tensor:
+ size = self.prompt_encoder.image_embedding_size
+ target_device = self.shared_image_embedding.positional_embedding.device
+ target_dtype = self.shared_image_embedding.positional_embedding.dtype
+ grid = torch.ones(size, device=target_device, dtype=target_dtype)
+ y_embed = grid.cumsum(dim=0) - 0.5
+ x_embed = grid.cumsum(dim=1) - 0.5
+ y_embed = y_embed / size[0]
+ x_embed = x_embed / size[1]
+
+ positional_embedding = self.shared_image_embedding(torch.stack([x_embed, y_embed], dim=-1))
+ return positional_embedding.permute(2, 0, 1).unsqueeze(0) # channel x height x width
+
+ @torch.no_grad()
+ def get_image_embeddings(
+ self,
+ pixel_values: torch.FloatTensor,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> list[torch.Tensor]:
+ r"""
+ Returns the image embeddings by passing the pixel values through the vision encoder.
+
+ Args:
+ pixel_values (`torch.FloatTensor` of shape `(batch_size, num_channels, height, width)`):
+ Input pixel values
+ """
+ batch_size = pixel_values.shape[0]
+ image_outputs = self.get_image_features(pixel_values, return_dict=True, **kwargs)
+ feature_maps = image_outputs.fpn_hidden_states
+
+ # add no memory embedding to the last feature map
+ feature_maps[-1] = feature_maps[-1] + self.no_memory_embedding
+
+ # reshape feature maps to the same shape as the backbone feature sizes
+ image_embeddings = [
+ feat.permute(1, 2, 0).view(batch_size, -1, *feat_size)
+ for feat, feat_size in zip(feature_maps, self.backbone_feature_sizes)
+ ]
+
+ return image_embeddings
+
+ @torch.no_grad()
+ def get_prompt_embeddings(
+ self,
+ input_points: torch.FloatTensor | None = None,
+ input_labels: torch.LongTensor | None = None,
+ input_boxes: torch.FloatTensor | None = None,
+ input_masks: torch.LongTensor | None = None,
+ ) -> tuple[torch.Tensor, torch.Tensor]:
+ r"""
+ Returns the prompt embeddings by passing the input points, labels, boxes and masks through the prompt encoder.
+
+ Args:
+ input_points (`torch.FloatTensor` of shape `(batch_size, point_batch_size, num_points_per_image, 2)`):
+ Optional input points for the prompt encoder. The padding of the point is automatically done by the
+ processor. `point_batch_size` refers to the number of masks that we want the model to predict per
+ point. The model will output `point_batch_size` times 3 masks in total.
+ input_labels (`torch.LongTensor` of shape `(batch_size, point_batch_size, num_points_per_image)`):
+ Optional input labels for the prompt encoder. The padding of the labels is automatically done by the
+ processor, or can be fed by the user.
+ input_boxes (`torch.FloatTensor` of shape `(batch_size, num_boxes_per_image, 4)`):
+ Optional input boxes for the prompt encoder. The padding of the boxes is automatically done by the
+ processor. users can also pass manually the input boxes.
+ input_masks (`torch.LongTensor` of shape `(batch_size, image_size, image_size)`):
+ Optional input masks for the prompt encoder.
+ """
+ prompt_output = self.prompt_encoder(
+ input_points=input_points,
+ input_labels=input_labels,
+ input_boxes=input_boxes,
+ input_masks=input_masks,
+ )
+ return prompt_output
+
+ @torch.inference_mode()
+ @auto_docstring(custom_intro="Propagate the objects through a streamed video frame.")
+ def forward(
+ self,
+ inference_session: EdgeTamVideoInferenceSession,
+ frame_idx: int | None = None,
+ frame: torch.Tensor | None = None,
+ reverse: bool = False,
+ **kwargs,
+ ) -> EdgeTamVideoSegmentationOutput:
+ r"""
+ inference_session (`EdgeTamVideoInferenceSession`):
+ The video inference session object.
+ frame_idx (`int`, *optional*):
+ The index of the frame on which to run inference. No need to provide when inferring
+ on a new streamed frame.
+ frame (`torch.Tensor`, *optional*):
+ The frame to process. Provide when streaming.
+ reverse (`bool`, *optional*, defaults to `False`):
+ Whether to propagate in reverse.
+ """
+ if frame is not None:
+ frame_idx = inference_session.add_new_frame(frame, frame_idx)
+
+ if frame is not None and inference_session.get_obj_num() == 0:
+ raise ValueError("No objects are provided for tracking; please add inputs first.")
+
+ num_objects = inference_session.get_obj_num()
+ pred_masks_per_obj = [None] * num_objects
+ object_score_logits_per_obj = [None] * num_objects
+ # Note: We avoid batched inference here because per-object inputs (clicks/masks)
+ # can differ across objects.
+ for obj_idx in range(num_objects):
+ obj_id = inference_session.obj_idx_to_id(obj_idx)
+ has_new_inputs = obj_id in inference_session.obj_with_new_inputs
+ has_cond_output = frame_idx in inference_session.output_dict_per_obj[obj_idx]["cond_frame_outputs"]
+ # If this object has no new inputs and this frame already has a
+ # conditioning output, reuse the cached masks instead of recomputing.
+ if (not has_new_inputs) and has_cond_output:
+ pred_masks = inference_session.get_output(obj_idx, frame_idx, "pred_masks", is_conditioning_frame=True)
+ object_score_logits = inference_session.get_output(
+ obj_idx, frame_idx, "object_score_logits", is_conditioning_frame=True
+ )
+ is_init_cond_frame = True
+ else:
+ # Defaults when there are no new inputs
+ is_init_cond_frame = False
+ point_inputs = None
+ mask_inputs = None
+
+ if has_new_inputs:
+ is_init_cond_frame = frame_idx not in inference_session.frames_tracked_per_obj[obj_idx]
+ if is_init_cond_frame:
+ reverse = False
+ point_inputs = inference_session.point_inputs_per_obj[obj_idx].get(frame_idx, None)
+ mask_inputs = inference_session.mask_inputs_per_obj[obj_idx].get(frame_idx, None)
+ if point_inputs is not None or mask_inputs is not None:
+ inference_session.obj_with_new_inputs.remove(obj_id)
+
+ current_out = self._run_single_frame_inference(
+ inference_session=inference_session,
+ obj_idx=obj_idx,
+ frame_idx=frame_idx,
+ batch_size=1, # run on the slice of a single object
+ is_init_cond_frame=is_init_cond_frame,
+ point_inputs=point_inputs,
+ mask_inputs=mask_inputs,
+ reverse=reverse,
+ run_mem_encoder=True,
+ streaming=frame is not None,
+ )
+ inference_session.store_output(
+ obj_idx, frame_idx, output_value=current_out, is_conditioning_frame=is_init_cond_frame
+ )
+ pred_masks = current_out["pred_masks"]
+ object_score_logits = current_out["object_score_logits"]
+
+ pred_masks_per_obj[obj_idx] = pred_masks
+ object_score_logits_per_obj[obj_idx] = object_score_logits.squeeze(-1)
+ if not is_init_cond_frame:
+ # only for tracked frames, not for initial conditioning frames
+ inference_session.frames_tracked_per_obj[obj_idx][frame_idx] = {"reverse": reverse}
+
+ # Resize the output mask to the original video resolution (we directly use
+ # the mask scores on GPU for output to avoid any CPU conversion in between)
+ if len(pred_masks_per_obj) > 1:
+ all_pred_masks = torch.cat(pred_masks_per_obj, dim=0)
+ all_object_score_logits = torch.cat(object_score_logits_per_obj, dim=0)
+ else:
+ all_pred_masks = pred_masks_per_obj[0]
+ all_object_score_logits = object_score_logits_per_obj[0]
+
+ return EdgeTamVideoSegmentationOutput(
+ object_ids=inference_session.obj_ids.copy(),
+ pred_masks=all_pred_masks,
+ object_score_logits=all_object_score_logits,
+ frame_idx=frame_idx,
+ )
+
+ @can_return_tuple
+ @auto_docstring
+ def get_image_features(
+ self,
+ pixel_values: torch.FloatTensor,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | EdgeTamVideoVisionEncoderOutput:
+ r"""
+ pixel_values (`torch.FloatTensor`):
+ Input pixel values of shape `(batch_size, num_channels, height, width)`.
+ """
+ vision_outputs: EdgeTamVideoVisionEncoderOutput = self.vision_encoder(pixel_values, return_dict=True, **kwargs)
+
+ feature_maps = vision_outputs.fpn_hidden_states
+ feature_maps_position_embeddings = vision_outputs.fpn_position_encoding
+
+ # precompute projected level 0 and level 1 features in SAM decoder
+ # to avoid running it again on every SAM click
+ feature_maps = list(feature_maps)
+ feature_maps[0] = self.mask_decoder.conv_s0(feature_maps[0])
+ feature_maps[1] = self.mask_decoder.conv_s1(feature_maps[1])
+
+ # flatten NxCxHxW to HWxNxC
+ feature_maps = [feature_map.flatten(2).permute(2, 0, 1) for feature_map in feature_maps]
+ feature_maps_position_embeddings = [
+ feature_map_position_embedding.flatten(2).permute(2, 0, 1)
+ for feature_map_position_embedding in feature_maps_position_embeddings
+ ]
+ vision_outputs.fpn_hidden_states = feature_maps
+ vision_outputs.fpn_position_encoding = feature_maps_position_embeddings
+
+ return vision_outputs
+
+ def _prepare_vision_features(
+ self,
+ inference_session: EdgeTamVideoInferenceSession,
+ frame_idx: int,
+ batch_size: int,
+ ) -> tuple[torch.Tensor, list[torch.Tensor]]:
+ """Prepare vision features for a frame."""
+
+ # Check if features are cached
+ if cached_features := inference_session.cache.get_vision_features(frame_idx):
+ vision_feats = cached_features["vision_feats"]
+ vision_pos_embeds = cached_features["vision_pos_embeds"]
+ else:
+ # Compute features using image encoder
+ image_batch = inference_session.get_frame(frame_idx).unsqueeze(0) # Add batch dimension
+ image_outputs = self.get_image_features(image_batch, return_dict=True)
+ vision_feats = image_outputs.fpn_hidden_states
+ vision_pos_embeds = image_outputs.fpn_position_encoding
+ # Cache features
+ inference_session.cache.cache_vision_features(
+ frame_idx, {"vision_feats": vision_feats, "vision_pos_embeds": vision_pos_embeds}
+ )
+
+ # Expand to batch size if needed
+ if batch_size > 1:
+ vision_feats = vision_feats.expand(batch_size, -1, -1, -1)
+ vision_pos_embeds = [pe.expand(batch_size, -1, -1, -1) for pe in vision_pos_embeds]
+
+ return vision_feats, vision_pos_embeds
+
+ def _single_frame_forward(
+ self,
+ pixel_values: torch.FloatTensor | None = None,
+ input_points: torch.FloatTensor | None = None,
+ input_labels: torch.LongTensor | None = None,
+ input_boxes: torch.FloatTensor | None = None,
+ input_masks: torch.LongTensor | None = None,
+ image_embeddings: torch.FloatTensor | None = None,
+ multimask_output: bool = True,
+ attention_similarity: torch.FloatTensor | None = None,
+ target_embedding: torch.FloatTensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> EdgeTamVideoImageSegmentationOutput:
+ """
+ input_points (`torch.FloatTensor` of shape `(batch_size, num_points, 2)`):
+ Input 2D spatial points, this is used by the prompt encoder to encode the prompt. Generally yields to much
+ better results. The points can be obtained by passing a list of list of list to the processor that will
+ create corresponding `torch` tensors of dimension 4. The first dimension is the image batch size, the
+ second dimension is the point batch size (i.e. how many segmentation masks do we want the model to predict
+ per input point), the third dimension is the number of points per segmentation mask (it is possible to pass
+ multiple points for a single mask), and the last dimension is the x (vertical) and y (horizontal)
+ coordinates of the point. If a different number of points is passed either for each image, or for each
+ mask, the processor will create "PAD" points that will correspond to the (0, 0) coordinate, and the
+ computation of the embedding will be skipped for these points using the labels.
+ input_labels (`torch.LongTensor` of shape `(batch_size, point_batch_size, num_points)`):
+ Input labels for the points, this is used by the prompt encoder to encode the prompt. According to the
+ official implementation, there are 3 types of labels
+
+ - `1`: the point is a point that contains the object of interest
+ - `0`: the point is a point that does not contain the object of interest
+ - `-1`: the point corresponds to the background
+
+ We added the label:
+
+ - `-10`: the point is a padding point, thus should be ignored by the prompt encoder
+
+ The padding labels should be automatically done by the processor.
+ input_boxes (`torch.FloatTensor` of shape `(batch_size, num_boxes, 4)`):
+ Input boxes for the points, this is used by the prompt encoder to encode the prompt. Generally yields to
+ much better generated masks. The boxes can be obtained by passing a list of list of list to the processor,
+ that will generate a `torch` tensor, with each dimension corresponding respectively to the image batch
+ size, the number of boxes per image and the coordinates of the top left and bottom right point of the box.
+ In the order (`x1`, `y1`, `x2`, `y2`):
+
+ - `x1`: the x coordinate of the top left point of the input box
+ - `y1`: the y coordinate of the top left point of the input box
+ - `x2`: the x coordinate of the bottom right point of the input box
+ - `y2`: the y coordinate of the bottom right point of the input box
+ input_masks (`torch.FloatTensor` of shape `(batch_size, image_size, image_size)`):
+ SAM model also accepts segmentation masks as input. The mask will be embedded by the prompt encoder to
+ generate a corresponding embedding, that will be fed later on to the mask decoder. These masks needs to be
+ manually fed by the user, and they need to be of shape (`batch_size`, `image_size`, `image_size`).
+ image_embeddings (`torch.FloatTensor` of shape `(batch_size, output_channels, window_size, window_size)`):
+ Image embeddings, this is used by the mask decoder to generate masks and iou scores. For more memory
+ efficient computation, users can first retrieve the image embeddings using the `get_image_embeddings`
+ method, and then feed them to the `forward` method instead of feeding the `pixel_values`.
+ multimask_output (`bool`, *optional*):
+ In the original implementation and paper, the model always outputs 3 masks per image (or per point / per
+ bounding box if relevant). However, it is possible to just output a single mask, that corresponds to the
+ "best" mask, by specifying `multimask_output=False`.
+ attention_similarity (`torch.FloatTensor`, *optional*):
+ Attention similarity tensor, to be provided to the mask decoder for target-guided attention in case the
+ model is used for personalization as introduced in [PerSAM](https://huggingface.co/papers/2305.03048).
+ target_embedding (`torch.FloatTensor`, *optional*):
+ Embedding of the target concept, to be provided to the mask decoder for target-semantic prompting in case
+ the model is used for personalization as introduced in [PerSAM](https://huggingface.co/papers/2305.03048).
+ """
+ if not ((pixel_values is None) ^ (image_embeddings is None)):
+ raise ValueError("Exactly one of pixel_values or image_embeddings must be provided.")
+ if input_points is not None and input_boxes is not None:
+ if input_points.shape[1] != input_boxes.shape[1]:
+ raise ValueError(
+ f"You should provide as many bounding boxes as input points per box. Got {input_points.shape[1]} and {input_boxes.shape[1]}."
+ )
+ elif input_points is not None:
+ num_objects = input_points.shape[1]
+ elif input_boxes is not None:
+ num_objects = input_boxes.shape[1]
+ elif input_masks is not None:
+ num_objects = input_masks.shape[1]
+ else:
+ num_objects = 1
+
+ image_positional_embeddings = self.get_image_wide_positional_embeddings()
+ # repeat with batch size
+ batch_size = pixel_values.shape[0] if pixel_values is not None else image_embeddings[-1].shape[0]
+ image_positional_embeddings = image_positional_embeddings.repeat(batch_size, 1, 1, 1)
+
+ vision_attentions = None
+ vision_hidden_states = None
+
+ if pixel_values is not None:
+ image_outputs = self.get_image_features(pixel_values, return_dict=True, **kwargs)
+ feature_maps = image_outputs.fpn_hidden_states
+ vision_hidden_states = image_outputs.hidden_states
+ vision_attentions = image_outputs.attentions
+
+ # add no memory embedding to the last feature map
+ feature_maps[-1] = feature_maps[-1] + self.no_memory_embedding
+
+ # reshape feature maps to the same shape as the backbone feature sizes
+ image_embeddings = [
+ feat.permute(1, 2, 0).view(batch_size, -1, *feat_size)
+ for feat, feat_size in zip(feature_maps, self.backbone_feature_sizes)
+ ]
+
+ if input_points is not None and input_labels is None:
+ input_labels = torch.ones_like(input_points[:, :, :, 0], dtype=torch.int, device=input_points.device)
+
+ if input_points is None and input_boxes is None:
+ # If no points are provide, pad with an empty point (with label -1)
+ input_points = torch.zeros(
+ batch_size, 1, 1, 2, dtype=image_embeddings[-1].dtype, device=image_embeddings[-1].device
+ )
+ input_labels = -torch.ones(batch_size, 1, 1, dtype=torch.int32, device=image_embeddings[-1].device)
+
+ if input_masks is not None:
+ # If mask_inputs is provided, downsize it into low-res mask input if needed
+ # and feed it as a dense mask prompt into the SAM mask encoder
+ if input_masks.shape[-2:] != self.prompt_encoder.mask_input_size:
+ input_masks = F.interpolate(
+ input_masks.float(),
+ size=self.prompt_encoder.mask_input_size,
+ align_corners=False,
+ mode="bilinear",
+ antialias=True, # use antialias for downsampling
+ ).to(input_masks.dtype)
+
+ sparse_embeddings, dense_embeddings = self.prompt_encoder(
+ input_points=input_points,
+ input_labels=input_labels,
+ input_boxes=input_boxes,
+ input_masks=input_masks,
+ )
+ low_res_multimasks, iou_scores, sam_output_tokens, object_score_logits = self.mask_decoder(
+ image_embeddings=image_embeddings[-1],
+ image_positional_embeddings=image_positional_embeddings,
+ sparse_prompt_embeddings=sparse_embeddings,
+ dense_prompt_embeddings=dense_embeddings,
+ multimask_output=multimask_output,
+ high_resolution_features=image_embeddings[:-1],
+ attention_similarity=attention_similarity,
+ target_embedding=target_embedding,
+ **kwargs,
+ )
+
+ is_obj_appearing = object_score_logits > 0
+ # Mask used for spatial memories is always a *hard* choice between obj and no obj,
+ # consistent with the actual mask prediction
+ low_res_multimasks = torch.where(
+ is_obj_appearing[:, None, None],
+ low_res_multimasks,
+ NO_OBJ_SCORE,
+ )
+
+ # convert masks from possibly bfloat16 (or float16) to float32
+ # (older PyTorch versions before 2.1 don't support `interpolate` on bf16)
+ high_res_multimasks = (
+ F.interpolate(
+ low_res_multimasks.squeeze(1).float(),
+ size=(self.image_size, self.image_size),
+ mode="bilinear",
+ align_corners=False,
+ )
+ .unsqueeze(1)
+ .to(low_res_multimasks.dtype)
+ )
+ sam_output_token = sam_output_tokens[:, :, 0]
+ if multimask_output:
+ # take the best mask prediction (with the highest IoU estimation)
+ best_iou_inds = torch.argmax(iou_scores, dim=-1)
+ batch_inds = torch.arange(batch_size, device=high_res_multimasks.device)
+ object_batch_inds = torch.arange(num_objects, device=high_res_multimasks.device)
+ low_res_masks = low_res_multimasks[batch_inds, object_batch_inds, best_iou_inds]
+ high_res_masks = high_res_multimasks[batch_inds, object_batch_inds, best_iou_inds]
+ if sam_output_tokens.size(2) > 1:
+ sam_output_token = sam_output_tokens[batch_inds, object_batch_inds, best_iou_inds]
+ else:
+ low_res_masks, high_res_masks = low_res_multimasks[:, :, 0], high_res_multimasks[:, :, 0]
+
+ # Extract object pointer from the SAM output token (with occlusion handling)
+ object_pointer = self.object_pointer_proj(sam_output_token)
+ lambda_is_obj_appearing = is_obj_appearing.to(object_pointer.dtype)
+
+ object_pointer = lambda_is_obj_appearing * object_pointer
+ object_pointer = object_pointer + (1 - lambda_is_obj_appearing) * self.no_object_pointer
+
+ return EdgeTamVideoImageSegmentationOutput(
+ iou_scores=iou_scores,
+ pred_masks=low_res_masks,
+ high_res_masks=high_res_masks,
+ object_pointer=object_pointer,
+ object_score_logits=object_score_logits,
+ image_embeddings=image_embeddings,
+ vision_hidden_states=vision_hidden_states,
+ vision_attentions=vision_attentions,
+ )
+
+ def _use_mask_as_output(
+ self,
+ backbone_features: torch.Tensor,
+ high_res_features: list[torch.Tensor],
+ mask_inputs: torch.Tensor,
+ ) -> EdgeTamVideoImageSegmentationOutput:
+ """
+ Directly turn binary `mask_inputs` into a output mask logits without using SAM.
+ (same input and output shapes as in forward above).
+ """
+ # Use -10/+20 as logits for neg/pos pixels (very close to 0/1 in prob after sigmoid).
+ out_scale, out_bias = 20.0, -10.0 # sigmoid(-10.0)=4.5398e-05
+ mask_inputs_float = mask_inputs.to(backbone_features[0].dtype)
+ high_res_masks = mask_inputs_float * out_scale + out_bias
+ low_res_masks = F.interpolate(
+ high_res_masks.float(),
+ size=(high_res_masks.size(-2) // 4, high_res_masks.size(-1) // 4),
+ align_corners=False,
+ mode="bilinear",
+ antialias=True, # use antialias for downsampling
+ ).to(backbone_features[0].dtype)
+ # a dummy IoU prediction of all 1's under mask input
+ iou_scores = mask_inputs.new_ones(mask_inputs.size(0), 1).to(backbone_features[0].dtype)
+ # produce an object pointer using the SAM decoder from the mask input
+ object_pointer = self._single_frame_forward(
+ input_masks=self.mask_downsample(mask_inputs_float.to(backbone_features[0].dtype)),
+ image_embeddings=high_res_features + [backbone_features],
+ ).object_pointer
+ # In this method, we are treating mask_input as output, e.g. using it directly to create spatial mem;
+ # Below, we follow the same design axiom to use mask_input to decide if obj appears or not instead of relying
+ # on the object_scores from the SAM decoder.
+ is_obj_appearing = torch.any(mask_inputs.flatten(1).float() > 0.0, dim=1)
+ is_obj_appearing = is_obj_appearing[..., None]
+ lambda_is_obj_appearing = is_obj_appearing.to(backbone_features[0].dtype)
+ object_score_logits = out_scale * lambda_is_obj_appearing + out_bias
+ object_pointer = lambda_is_obj_appearing * object_pointer
+ object_pointer = object_pointer + (1 - lambda_is_obj_appearing) * self.no_object_pointer
+ return EdgeTamVideoImageSegmentationOutput(
+ iou_scores=iou_scores,
+ pred_masks=low_res_masks,
+ high_res_masks=high_res_masks,
+ object_pointer=object_pointer,
+ object_score_logits=object_score_logits,
+ image_embeddings=high_res_features + [backbone_features],
+ )
+
+ def _select_closest_cond_frames(self, frame_idx, cond_frame_outputs, max_cond_frame_num):
+ """
+ Select up to `max_cond_frame_num` conditioning frames from `cond_frame_outputs`
+ that are temporally closest to the current frame at `frame_idx`. Here, we take
+ - a) the closest conditioning frame before `frame_idx` (if any);
+ - b) the closest conditioning frame after `frame_idx` (if any);
+ - c) any other temporally closest conditioning frames until reaching a total
+ of `max_cond_frame_num` conditioning frames.
+
+ Outputs:
+ - selected_outputs: selected items (keys & values) from `cond_frame_outputs`.
+ - unselected_outputs: items (keys & values) not selected in `cond_frame_outputs`.
+ """
+ if max_cond_frame_num == -1 or len(cond_frame_outputs) <= max_cond_frame_num:
+ selected_outputs = cond_frame_outputs
+ unselected_outputs = {}
+ else:
+ selected_outputs = {}
+ # the closest conditioning frame before `frame_idx` (if any)
+ idx_before = max((t for t in cond_frame_outputs if t < frame_idx), default=None)
+ if idx_before is not None:
+ selected_outputs[idx_before] = cond_frame_outputs[idx_before]
+
+ # the closest conditioning frame after `frame_idx` (if any)
+ idx_after = min((t for t in cond_frame_outputs if t >= frame_idx), default=None)
+ if idx_after is not None:
+ selected_outputs[idx_after] = cond_frame_outputs[idx_after]
+
+ # add other temporally closest conditioning frames until reaching a total
+ # of `max_cond_frame_num` conditioning frames.
+ num_remain = max_cond_frame_num - len(selected_outputs)
+ inds_remain = sorted(
+ (t for t in cond_frame_outputs if t not in selected_outputs),
+ key=lambda x: abs(x - frame_idx),
+ )[:num_remain]
+ selected_outputs.update((t, cond_frame_outputs[t]) for t in inds_remain)
+ unselected_outputs = {t: v for t, v in cond_frame_outputs.items() if t not in selected_outputs}
+
+ return selected_outputs, unselected_outputs
+
+ def _gather_memory_frame_outputs(
+ self,
+ inference_session: EdgeTamVideoInferenceSession,
+ obj_idx: int,
+ frame_idx: int,
+ track_in_reverse_time: bool = False,
+ ) -> list[tuple[int, dict]]:
+ """
+ Get memory frames from conditioning and non-conditioning outputs.
+
+ Returns:
+ List of (relative_temporal_offset, output_data) tuples.
+ """
+ temporal_positions_and_previous_outputs = []
+
+ # Add conditioning frame outputs (limited by max_cond_frame_num)
+ conditioning_outputs = inference_session.output_dict_per_obj[obj_idx]["cond_frame_outputs"]
+ if not conditioning_outputs:
+ raise ValueError(
+ "maskmem_features in conditioning outputs cannot be empty when not is_initial_conditioning_frame"
+ )
+ conditioning_outputs, unselected_conditioning_outputs = self._select_closest_cond_frames(
+ frame_idx, conditioning_outputs, max_cond_frame_num=self.config.max_cond_frame_num
+ )
+
+ # Store (temporal_position, output_data) tuples
+ temporal_positions_and_previous_outputs = [(0, out) for out in conditioning_outputs.values()]
+
+ # Add non-conditioning memory frames (up to self.num_maskmem - 1)
+ # These are typically frames tracked by the model without direct user input.
+ # Frames are selected with a stride, prioritizing the most recent ones. Here we only support stride = 1 for simplicity.
+ for relative_temporal_offset in range(self.num_maskmem - 1, 0, -1):
+ # relative_temporal_offset: how many frames before (or after if reversing) the current frame
+ if not track_in_reverse_time:
+ previous_frame_idx = frame_idx - relative_temporal_offset
+ else:
+ previous_frame_idx = frame_idx + relative_temporal_offset
+
+ # check if the output is already stored without using get_output to avoid unnecessary memory transfers between CPU and GPU
+ output_data = inference_session.output_dict_per_obj[obj_idx]["non_cond_frame_outputs"].get(
+ previous_frame_idx, unselected_conditioning_outputs.get(previous_frame_idx, None)
+ )
+
+ temporal_positions_and_previous_outputs.append((relative_temporal_offset, output_data))
+
+ return temporal_positions_and_previous_outputs
+
+ def _build_memory_attention_inputs(
+ self,
+ temporal_positions_and_previous_outputs: list[tuple[int, dict]],
+ device: torch.device,
+ ) -> tuple[list[torch.Tensor], list[torch.Tensor]]:
+ """
+ Concatenate memory features and positional embeddings from previous frames.
+
+ Returns:
+ Tuple of (memories_to_concatenate, memory_positional_embeddings_to_concatenate).
+ """
+ memories_to_concatenate = []
+ memory_positional_embeddings_to_concatenate = []
+
+ for relative_temporal_offset, prev_output_data in temporal_positions_and_previous_outputs:
+ if prev_output_data is None:
+ continue # Skip if no output data for this temporal position (e.g., padding frames)
+
+ # Load memory features (potentially from CPU to GPU)
+ # Features are flattened: (Batch, Channels, H, W) -> (H*W, Batch, Channels)
+ memory_features = prev_output_data["maskmem_features"].to(device, non_blocking=True)
+ memories_to_concatenate.append(memory_features.permute(1, 0, 2))
+
+ # Spatial positional encoding (potentially from CPU to GPU)
+ spatial_memory_pos_embed = prev_output_data["maskmem_pos_enc"].to(device, non_blocking=True)
+ spatial_memory_pos_embed = spatial_memory_pos_embed.squeeze(1).permute(1, 0, 2)
+
+ # Add temporal positional encoding
+ # self.memory_temporal_positional_encoding shape: (NumMaskMem, 1, 1, MemDim)
+ combined_memory_pos_embed = (
+ spatial_memory_pos_embed + self.memory_temporal_positional_encoding[relative_temporal_offset - 1]
+ )
+ memory_positional_embeddings_to_concatenate.append(combined_memory_pos_embed)
+
+ return memories_to_concatenate, memory_positional_embeddings_to_concatenate
+
+ def _get_object_pointers(
+ self,
+ inference_session: EdgeTamVideoInferenceSession,
+ obj_idx: int,
+ frame_idx: int,
+ num_total_frames: int,
+ device: torch.device,
+ track_in_reverse_time: bool = False,
+ streaming: bool = False,
+ ) -> tuple[list[int], list[torch.Tensor], int]:
+ """
+ Get object pointers and their positional embeddings from past frames.
+
+ Returns:
+ Tuple of (temporal_offsets, pointer_tokens, max_object_pointers_to_use).
+ """
+ temporal_position_sign_multiplier = -1 if track_in_reverse_time else 1
+
+ # Determine max object pointers to use
+ if streaming:
+ max_object_pointers_to_use = self.config.max_object_pointers_in_encoder
+ else:
+ max_object_pointers_to_use = min(num_total_frames, self.config.max_object_pointers_in_encoder)
+
+ temporal_offsets: list[int] = []
+ pointer_tokens: list[torch.Tensor] = []
+
+ # Add object pointers from selected conditioning frames
+ # Optionally, only include pointers from past frames during evaluation
+ conditioning_outputs = inference_session.output_dict_per_obj[obj_idx]["cond_frame_outputs"]
+ eligible_conditioning_outputs = conditioning_outputs
+ if not self.training:
+ eligible_conditioning_outputs = {
+ temporal_idx: out
+ for temporal_idx, out in conditioning_outputs.items()
+ if (temporal_idx >= frame_idx if track_in_reverse_time else temporal_idx <= frame_idx)
+ }
+
+ for temporal_idx, out_data in eligible_conditioning_outputs.items():
+ temporal_difference = (frame_idx - temporal_idx) * temporal_position_sign_multiplier
+ temporal_offsets.append(temporal_difference)
+ pointer_tokens.append(out_data["object_pointer"].to(device))
+
+ # Add object pointers from non-conditioning frames (up to max_object_pointers_to_use - 1)
+ for t_diff_offset in range(1, max_object_pointers_to_use):
+ ref_frame_idx = frame_idx + t_diff_offset if track_in_reverse_time else frame_idx - t_diff_offset
+ if ref_frame_idx < 0 or (
+ not streaming and num_total_frames is not None and ref_frame_idx >= num_total_frames
+ ):
+ break # Stop if frame index is out of bounds
+
+ # check if the output is already stored without using get_output to avoid unnecessary memory transfers between CPU and GPU
+ out_data = inference_session.output_dict_per_obj[obj_idx]["non_cond_frame_outputs"].get(
+ ref_frame_idx, None
+ )
+ if out_data is not None:
+ temporal_offsets.append(t_diff_offset)
+ pointer_tokens.append(out_data["object_pointer"].to(device))
+
+ return temporal_offsets, pointer_tokens, max_object_pointers_to_use
+
+ def _process_object_pointers(
+ self,
+ temporal_offsets: list[int],
+ pointer_tokens: list[torch.Tensor],
+ max_object_pointers_to_use: int,
+ batch_size: int,
+ num_channels: int,
+ device: torch.device,
+ ) -> tuple[torch.Tensor, torch.Tensor]:
+ """
+ Process object pointers and compute their positional embeddings.
+
+ Returns:
+ Tuple of (object_pointers, object_pointers_pos_embed).
+ """
+ if not pointer_tokens:
+ return None, None
+
+ # Stack object pointers: List of (Batch, Channels) -> (SeqLen_ptr, Batch, Channels)
+ object_pointers = torch.stack(pointer_tokens, dim=0)
+
+ if self.config.enable_temporal_pos_encoding_for_object_pointers:
+ max_temporal_diff = float(max_object_pointers_to_use - 1)
+ # Determine dimensionality for temporal positional encoding of pointers
+ pointer_tpos_dim = num_channels
+
+ # Normalize temporal differences before sine PE calculation
+ normalized_temporal_diffs = (
+ torch.tensor(temporal_offsets, device=device, dtype=torch.float32) / max_temporal_diff
+ )
+ sine_pe = get_1d_sine_pe(normalized_temporal_diffs, dim=pointer_tpos_dim).to(object_pointers.dtype)
+ projected_sine_pe = self.temporal_positional_encoding_projection_layer(sine_pe)
+ object_pointers_pos_embed = projected_sine_pe.unsqueeze(1).expand(-1, batch_size, self.mem_dim)
+ else:
+ object_pointers_pos_embed = object_pointers.new_zeros(
+ len(temporal_offsets), batch_size, self.mem_dim, dtype=object_pointers.dtype
+ )
+
+ if self.mem_dim < num_channels:
+ # If memory dimension is smaller, reshape/split pointers and repeat positional encoding
+ num_splits = num_channels // self.mem_dim
+ object_pointers = object_pointers.reshape(-1, batch_size, num_splits, self.mem_dim)
+ object_pointers = object_pointers.permute(0, 2, 1, 3).flatten(
+ 0, 1
+ ) # (SeqLen_ptr*num_splits, Batch, MemDim)
+ object_pointers_pos_embed = object_pointers_pos_embed.repeat_interleave(num_splits, dim=0)
+
+ return object_pointers, object_pointers_pos_embed
+
+ def _prepare_memory_conditioned_features(
+ self,
+ inference_session: EdgeTamVideoInferenceSession,
+ frame_idx: int,
+ obj_idx: int,
+ is_initial_conditioning_frame: bool,
+ current_vision_features: list[torch.Tensor],
+ current_vision_positional_embeddings: list[torch.Tensor],
+ num_total_frames: int,
+ track_in_reverse_time: bool = False,
+ streaming: bool = False,
+ ) -> torch.Tensor:
+ """
+ Fuse current frame's visual features with memory from previous frames for enhanced object tracking.
+
+ This method conditions the current frame's visual features on temporal memory from previous frames,
+ enabling consistent object tracking across video sequences. For initial conditioning frames, it uses
+ no-memory embeddings. For subsequent frames, it retrieves and integrates memory features from both
+ conditioning frames (user interactions) and non-conditioning frames (tracked results) via cross-attention.
+
+ Args:
+ inference_session (`EdgeTamVideoInferenceSession`):
+ The video inference session object.
+ frame_idx (`int`):
+ Index of the current frame being processed.
+ obj_idx (`int`):
+ Index of the object being processed.
+ is_initial_conditioning_frame (`bool`):
+ Whether this is an initial conditioning frame with user inputs (True) or a subsequent
+ tracking frame (False).
+ current_vision_features (`torch.Tensor`):
+ Highest-level vision features of shape `(seq_len, batch_size, channels)`.
+ current_vision_positional_embeddings (`torch.Tensor`):
+ Positional embedding tensors corresponding to the highest-level vision features.
+ num_total_frames (`int`):
+ Total number of frames in the video sequence.
+ track_in_reverse_time (`bool`, *optional*, defaults to `False`):
+ Whether tracking is performed in reverse temporal order.
+ streaming (`bool`, *optional*, defaults to `False`):
+ Whether this is streaming inference mode.
+
+ Returns:
+ `torch.Tensor`: Memory-conditioned feature tensor of shape `(batch_size, channels, height, width)`
+ suitable for input to the SAM decoder.
+ """
+ # Get dimensions from the highest-level (lowest-resolution) feature map
+ batch_size = current_vision_features.size(1)
+ num_channels = self.hidden_dim
+ height, width = self.backbone_feature_sizes[-1]
+ device = current_vision_features.device
+
+ # If memory is disabled (e.g., for single image SAM), return current features directly.
+ if self.num_maskmem == 0:
+ # Permute (SeqLen, Batch, Channels) -> (Batch, Channels, SeqLen) then view as (Batch, Channels, Height, Width)
+ # Assuming SeqLen = Height * Width for the last feature map
+ current_feature_map = current_vision_features.permute(1, 2, 0).view(
+ batch_size, num_channels, height, width
+ )
+ return current_feature_map
+
+ # Step 1: Handle initial conditioning frames
+ if is_initial_conditioning_frame:
+ # For initial conditioning frames, no prior memory is used directly in this block.
+ # If configured, directly add a learnable "no memory" embedding.
+ # current_vision_features has shape (SeqLen, Batch, Channels)
+ conditioned_feature_map_flat = current_vision_features + self.no_memory_embedding
+ # Reshape to (Batch, Channels, Height, Width)
+ conditioned_feature_map = conditioned_feature_map_flat.permute(1, 2, 0).view(
+ batch_size, num_channels, height, width
+ )
+ return conditioned_feature_map
+
+ # Step 2: Get memory frames and concatenate their features
+ temporal_positions_and_previous_outputs = self._gather_memory_frame_outputs(
+ inference_session, obj_idx, frame_idx, track_in_reverse_time
+ )
+
+ memories_to_concatenate, memory_positional_embeddings_to_concatenate = self._build_memory_attention_inputs(
+ temporal_positions_and_previous_outputs, device
+ )
+ num_spatial_memory_tokens = len(memories_to_concatenate)
+
+ # Step 3: Get and process object pointers
+ temporal_offsets, pointer_tokens, max_object_pointers_to_use = self._get_object_pointers(
+ inference_session, obj_idx, frame_idx, num_total_frames, device, track_in_reverse_time, streaming
+ )
+
+ num_object_pointer_tokens = 0
+ if pointer_tokens:
+ object_pointers, object_pointers_pos_embed = self._process_object_pointers(
+ temporal_offsets, pointer_tokens, max_object_pointers_to_use, batch_size, num_channels, device
+ )
+
+ if object_pointers is not None:
+ memories_to_concatenate.append(object_pointers)
+ memory_positional_embeddings_to_concatenate.append(object_pointers_pos_embed)
+ num_object_pointer_tokens = object_pointers.shape[0]
+
+ # Step 4: Concatenate all retrieved memories and their positional embeddings
+ combined_memory = torch.cat(memories_to_concatenate, dim=0)
+ combined_memory_positional_embeddings = torch.cat(memory_positional_embeddings_to_concatenate, dim=0)
+
+ # Step 5: Forward through the memory attention mechanism
+ conditioned_feature_map_flat = self.memory_attention(
+ current_vision_features=current_vision_features,
+ current_vision_position_embeddings=current_vision_positional_embeddings,
+ memory=combined_memory,
+ memory_posision_embeddings=combined_memory_positional_embeddings, # Corrected typo from API
+ num_object_pointer_tokens=num_object_pointer_tokens,
+ num_spatial_memory_tokens=num_spatial_memory_tokens,
+ )
+
+ # Reshape from (Batch, H*W, Channels) to (Batch, Channels, Height, Width)
+ conditioned_feature_map = (
+ conditioned_feature_map_flat.squeeze(1).permute(0, 2, 1).view(batch_size, num_channels, height, width)
+ )
+ return conditioned_feature_map
+
+ def _use_multimask(self, is_init_cond_frame: bool, point_inputs: dict | None) -> bool:
+ """Whether to use multimask output in the SAM head."""
+ num_pts = 0 if point_inputs is None else point_inputs["point_labels"].size(2)
+ multimask_output = (
+ self.config.multimask_output_in_sam
+ and (is_init_cond_frame or self.config.multimask_output_for_tracking)
+ and (self.config.multimask_min_pt_num <= num_pts <= self.config.multimask_max_pt_num)
+ )
+ return multimask_output
+
+ def _run_single_frame_inference(
+ self,
+ inference_session: EdgeTamVideoInferenceSession,
+ frame_idx: int,
+ obj_idx: int,
+ batch_size: int,
+ is_init_cond_frame: bool,
+ point_inputs: torch.Tensor | None,
+ mask_inputs: torch.Tensor | None,
+ reverse: bool,
+ run_mem_encoder: bool,
+ prev_sam_mask_logits: torch.Tensor | None = None,
+ streaming: bool = False,
+ ) -> dict[str, Any]:
+ """
+ Perform a single tracking step for video object segmentation.
+
+ Args:
+ inference_session (`EdgeTamVideoInferenceSession`):
+ The video inference session object.
+ frame_idx (`int`):
+ Index of the current frame.
+ obj_idx (`int`):
+ Index of the current object.
+ batch_size (`int`):
+ Batch size of the current frame.
+ is_init_cond_frame (`bool`):
+ Whether this is an initial conditioning frame with user inputs.
+ point_inputs (`dict`, *optional*):
+ Point prompt inputs for the current frame.
+ mask_inputs (`torch.Tensor`, *optional*):
+ Mask prompt inputs for the current frame.
+ reverse (`bool`, *optional*, defaults to `False`):
+ Whether to track in reverse time order.
+ run_mem_encoder (`bool`, *optional*, defaults to `True`):
+ Whether to run the memory encoder on predicted masks.
+ prev_sam_mask_logits (`torch.Tensor`, *optional*):
+ Previously predicted SAM mask logits that can be fed with new clicks.
+ streaming (`bool`, *optional*, defaults to `False`):
+ Whether this is streaming inference.
+
+ Returns:
+ `dict`: Dictionary containing the tracking results for the current frame, including:
+ - pred_masks: Predicted low-resolution masks.
+ - object_pointer: Object pointer for memory.
+ - object_score_logits: Object score logits (inference only).
+ - maskmem_features: Memory features for future frames.
+ - maskmem_pos_enc: Memory positional encodings.
+ """
+ # Retrieve correct image features
+ current_vision_feats, current_vision_pos_embeds = self._prepare_vision_features(
+ inference_session, frame_idx, batch_size
+ )
+ # point and mask should not appear as input simultaneously on the same frame
+ if point_inputs is not None and mask_inputs is not None:
+ raise ValueError(
+ "point_inputs and mask_inputs should not appear as input simultaneously on the same frame"
+ )
+ # High-resolution feature maps for the SAM head, reshape (HW)BC => BCHW
+ if len(current_vision_feats) > 1:
+ high_res_features = [
+ x.permute(1, 2, 0).view(x.size(1), x.size(2), *s)
+ for x, s in zip(current_vision_feats[:-1], self.backbone_feature_sizes[:-1])
+ ]
+ else:
+ high_res_features = None
+ if mask_inputs is not None:
+ # We directly output the mask input (see it as a GT mask) without using a SAM prompt encoder + mask decoder.
+ pix_feat = current_vision_feats[-1].permute(1, 2, 0)
+ pix_feat = pix_feat.view(-1, self.hidden_dim, *self.backbone_feature_sizes[-1])
+ sam_outputs = self._use_mask_as_output(pix_feat, high_res_features, mask_inputs)
+ else:
+ # fused the visual feature with previous memory features in the memory bank
+ pix_feat = self._prepare_memory_conditioned_features(
+ inference_session=inference_session,
+ frame_idx=frame_idx,
+ obj_idx=obj_idx,
+ is_initial_conditioning_frame=is_init_cond_frame,
+ current_vision_features=current_vision_feats[-1],
+ current_vision_positional_embeddings=current_vision_pos_embeds[-1],
+ num_total_frames=inference_session.num_frames,
+ track_in_reverse_time=reverse,
+ streaming=streaming,
+ )
+ # apply SAM-style segmentation head
+ # here we might feed previously predicted low-res SAM mask logits into the SAM mask decoder,
+ # e.g. in demo where such logits come from earlier interaction instead of correction sampling
+ # (in this case, any `mask_inputs` shouldn't reach here as they are sent to _use_mask_as_output instead)
+ if prev_sam_mask_logits is not None:
+ mask_inputs = prev_sam_mask_logits
+ multimask_output = self._use_multimask(is_init_cond_frame, point_inputs)
+ sam_outputs = self._single_frame_forward(
+ pixel_values=None, # Vision features already computed
+ input_points=point_inputs["point_coords"] if point_inputs is not None else None,
+ input_labels=point_inputs["point_labels"] if point_inputs is not None else None,
+ input_masks=mask_inputs,
+ image_embeddings=high_res_features + [pix_feat],
+ multimask_output=multimask_output,
+ )
+
+ # Finally run the memory encoder on the predicted mask to encode
+ # it into a new memory feature (which will be used to condition vision features in future frames)
+ maskmem_features = None
+ maskmem_pos_enc = None
+ if run_mem_encoder and self.num_maskmem > 0:
+ maskmem_features, maskmem_pos_enc = self._encode_new_memory(
+ current_vision_feats=current_vision_feats[-1],
+ pred_masks_high_res=sam_outputs.high_res_masks,
+ object_score_logits=sam_outputs.object_score_logits,
+ is_mask_from_pts=(point_inputs is not None or mask_inputs is not None),
+ )
+
+ current_out = {
+ "pred_masks": sam_outputs.pred_masks,
+ "object_pointer": sam_outputs.object_pointer,
+ "maskmem_features": maskmem_features if maskmem_features is not None else None,
+ "maskmem_pos_enc": maskmem_pos_enc,
+ }
+ if not self.training:
+ current_out["object_score_logits"] = sam_outputs.object_score_logits
+
+ return current_out
+
+ def _encode_new_memory(
+ self,
+ current_vision_feats: torch.Tensor,
+ pred_masks_high_res: torch.Tensor,
+ object_score_logits: torch.Tensor,
+ is_mask_from_pts: bool,
+ ) -> tuple[torch.Tensor, list[torch.Tensor]]:
+ """Encode the current image and its prediction into a memory feature."""
+ batch_size = current_vision_feats.size(1) # batch size on this frame
+ channels = self.hidden_dim
+ height, width = self.backbone_feature_sizes[-1] # top-level (lowest-resolution) feature size
+ # top-level feature, (HW)BC => BCHW
+ pix_feat = current_vision_feats.permute(1, 2, 0).view(batch_size, channels, height, width)
+ if is_mask_from_pts and not self.training:
+ # binarize the mask logits
+ mask_for_mem = (pred_masks_high_res > 0).to(pred_masks_high_res.dtype)
+ else:
+ # apply sigmoid on the raw mask logits to turn them into range (0, 1)
+ mask_for_mem = torch.sigmoid(pred_masks_high_res)
+ # apply scale and bias terms to the sigmoid probabilities
+ mask_for_mem = mask_for_mem * self.config.sigmoid_scale_for_mem_enc
+ mask_for_mem = mask_for_mem + self.config.sigmoid_bias_for_mem_enc
+
+ maskmem_features, maskmem_pos_enc = self.memory_encoder(
+ pix_feat,
+ mask_for_mem,
+ )
+ # add a no-object embedding to the spatial memory to indicate that the frame
+ # is predicted to be occluded (i.e. no object is appearing in the frame)
+ if self.occlusion_spatial_embedding_parameter is not None:
+ is_obj_appearing = (object_score_logits > 0).float()
+ maskmem_features += (1 - is_obj_appearing[..., None]) * self.occlusion_spatial_embedding_parameter[
+ ..., None, None
+ ].expand(*maskmem_features.shape)
+
+ maskmem_pos_enc = maskmem_pos_enc.to(pred_masks_high_res.dtype)
+ maskmem_features, maskmem_pos_enc = self.spatial_perceiver(maskmem_features, maskmem_pos_enc)
+ maskmem_features = maskmem_features.to(pred_masks_high_res.dtype)
+ maskmem_pos_enc = maskmem_pos_enc.to(pred_masks_high_res.dtype)
+
+ return maskmem_features, maskmem_pos_enc
+
+ @torch.inference_mode()
+ @auto_docstring(
+ custom_intro="""
+ Propagate the objects through the video frames. Used when initializing an inference session with a whole video.
+ Yields EdgeTamVideoSegmentationOutput for each frame.
+ """
+ )
+ def propagate_in_video_iterator(
+ self,
+ inference_session: EdgeTamVideoInferenceSession,
+ start_frame_idx: int | None = None,
+ max_frame_num_to_track: int | None = None,
+ reverse: bool = False,
+ show_progress_bar: bool = False,
+ ) -> Iterator[EdgeTamVideoSegmentationOutput]:
+ r"""
+ inference_session (`EdgeTamVideoInferenceSession`):
+ The video inference session object.
+ start_frame_idx (`int`, *optional*):
+ The starting frame index for propagation.
+ Need to be provided if `forward` hasn't been called on new inputs yet.
+ If not provided, the starting frame index will be the earliest frame with input points.
+ max_frame_num_to_track (`int`, *optional*):
+ The maximum number of frames to track.
+ reverse (`bool`, *optional*, defaults to `False`):
+ Whether to propagate in reverse.
+ show_progress_bar (`bool`, *optional*, defaults to `False`):
+ Whether to show a progress bar during propagation.
+ """
+ num_frames = inference_session.num_frames
+
+ # set start index, end index, and processing order
+ if start_frame_idx is None:
+ # default: start from the earliest frame with input points
+ frames_with_inputs = [
+ frame_idx
+ for obj_output_dict in inference_session.output_dict_per_obj.values()
+ for frame_idx in obj_output_dict["cond_frame_outputs"]
+ ]
+ if not frames_with_inputs:
+ raise ValueError(
+ "Cannot determine the starting frame index; please specify it manually, or run inference on a frame with inputs first."
+ )
+ start_frame_idx = min(frames_with_inputs)
+ if max_frame_num_to_track is None:
+ # default: track all the frames in the video
+ max_frame_num_to_track = num_frames
+ if reverse:
+ end_frame_idx = max(start_frame_idx - max_frame_num_to_track, 0)
+ if start_frame_idx > 0:
+ processing_order = range(start_frame_idx, end_frame_idx - 1, -1)
+ else:
+ processing_order = [] # skip reverse tracking if starting from frame 0
+ else:
+ end_frame_idx = min(start_frame_idx + max_frame_num_to_track, num_frames - 1)
+ processing_order = range(start_frame_idx, end_frame_idx + 1)
+
+ for frame_idx in tqdm(processing_order, desc="propagate in video", disable=not show_progress_bar):
+ edgetam_video_output = self(inference_session, frame_idx=frame_idx, reverse=reverse)
+ yield edgetam_video_output
+
+
+__all__ = ["EdgeTamVideoModel", "EdgeTamVideoInferenceSession", "EdgeTamVideoPreTrainedModel"]
diff --git a/third_party/transformers/src/transformers/models/edgetam_video/modular_edgetam_video.py b/third_party/transformers/src/transformers/models/edgetam_video/modular_edgetam_video.py
new file mode 100644
index 0000000000000000000000000000000000000000..8a59ce04a85a3513e740925b0da8e1d69d0047cc
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/edgetam_video/modular_edgetam_video.py
@@ -0,0 +1,1457 @@
+# Copyright 2025 the HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import math
+from collections.abc import Callable
+from typing import Any
+
+import torch
+import torch.nn as nn
+import torch.nn.functional as F
+from huggingface_hub.dataclasses import strict
+from torch import Tensor
+
+from ... import initialization as init
+from ...activations import ACT2FN
+from ...configuration_utils import PreTrainedConfig
+from ...modeling_flash_attention_utils import FlashAttentionKwargs
+from ...modeling_utils import ALL_ATTENTION_FUNCTIONS
+from ...processing_utils import Unpack
+from ...pytorch_utils import compile_compatible_method_lru_cache
+from ...utils import auto_docstring
+from ...utils.output_capturing import OutputRecorder
+from ..auto import CONFIG_MAPPING, AutoConfig
+from ..sam2.modeling_sam2 import eager_attention_forward, window_partition
+from ..sam2_video.configuration_sam2_video import (
+ Sam2VideoMaskDecoderConfig,
+ Sam2VideoPromptEncoderConfig,
+)
+from ..sam2_video.modeling_sam2_video import (
+ Sam2VideoAttention,
+ Sam2VideoFeedForward,
+ Sam2VideoImageSegmentationOutput,
+ Sam2VideoInferenceSession,
+ Sam2VideoLayerNorm,
+ Sam2VideoMemoryAttention,
+ Sam2VideoMemoryEncoder,
+ Sam2VideoMemoryFuserCXBlock,
+ Sam2VideoModel,
+ Sam2VideoPositionEmbeddingSine,
+ Sam2VideoPreTrainedModel,
+ Sam2VideoSegmentationOutput,
+ Sam2VideoTwoWayAttentionBlock,
+ Sam2VideoVisionEncoderOutput,
+ Sam2VideoVisionRotaryEmbedding,
+ rotate_pairwise,
+)
+
+
+@auto_docstring(checkpoint="yonigozlan/EdgeTAM-hf")
+@strict
+class EdgeTamVideoPromptEncoderConfig(Sam2VideoPromptEncoderConfig):
+ pass
+
+
+@auto_docstring(checkpoint="yonigozlan/EdgeTAM-hf")
+@strict
+class EdgeTamVideoMaskDecoderConfig(Sam2VideoMaskDecoderConfig):
+ pass
+
+
+@auto_docstring(checkpoint="yonigozlan/EdgeTAM-hf")
+@strict
+class EdgeTamVideoConfig(PreTrainedConfig):
+ r"""
+ prompt_encoder_config (Union[`dict`, `EdgeTamVideoPromptEncoderConfig`], *optional*):
+ Dictionary of configuration options used to initialize [`EdgeTamVideoPromptEncoderConfig`].
+ mask_decoder_config (Union[`dict`, `EdgeTamVideoMaskDecoderConfig`], *optional*):
+ Dictionary of configuration options used to initialize [`EdgeTamMaskDecoderConfig`].
+ num_maskmem (`int`, *optional*, defaults to 7):
+ The number of memory slots for the mask memory.
+ sigmoid_scale_for_mem_enc (`float`, *optional*, defaults to 20.0):
+ Scale factor for the sigmoid function in the memory encoder.
+ sigmoid_bias_for_mem_enc (`float`, *optional*, defaults to -10.0):
+ Bias for the sigmoid function in the memory encoder.
+ enable_occlusion_spatial_embedding (`bool`, *optional*, defaults to `True`):
+ Whether to enable spatial embedding for occlusions.
+ multimask_output_in_sam (`bool`, *optional*, defaults to `True`):
+ Whether to output multiple masks from the SAM head.
+ multimask_min_pt_num (`int`, *optional*, defaults to 0):
+ The minimum number of points to trigger multimask output.
+ multimask_max_pt_num (`int`, *optional*, defaults to 1):
+ The maximum number of points to trigger multimask output.
+ multimask_output_for_tracking (`bool`, *optional*, defaults to `True`):
+ Whether to use multimask output for tracking.
+ max_object_pointers_in_encoder (`int`, *optional*, defaults to 16):
+ The maximum number of object pointers in the encoder.
+ max_cond_frame_num (`int`, *optional*, defaults to -1):
+ Maximum number of conditioning frames to use in memory attention. Set to -1 to use all conditioning frames.
+ enable_temporal_pos_encoding_for_object_pointers (`bool`, *optional*, defaults to `True`):
+ Whether to enable temporal positional encoding for object pointers.
+ memory_attention_hidden_size (`int`, *optional*, defaults to 256):
+ Dimensionality of the memory attention hidden states.
+ memory_attention_num_layers (`int`, *optional*, defaults to 2):
+ The number of layers in the memory attention module.
+ memory_attention_num_attention_heads (`int`, *optional*, defaults to 1):
+ Number of attention heads for each attention layer in the memory attention.
+ memory_attention_downsample_rate (`int`, *optional*, defaults to 1):
+ The downsample rate for the attention layers.
+ memory_attention_mlp_hidden_size (`int`, *optional*, defaults to 2048):
+ The dimension of the feedforward network in the memory attention module.
+ memory_attention_mlp_hidden_act (`str`, *optional*, defaults to `"relu"`):
+ The non-linear activation function in the feedforward network in the memory attention module.
+ memory_attention_dropout (`float`, *optional*, defaults to 0.1):
+ The dropout rate for the memory attention module.
+ memory_attention_rope_theta (`float`, *optional*, defaults to 10000):
+ The Rope theta parameter.
+ memory_attention_rope_feat_sizes (`Tuple[int, int]`, *optional*, defaults to `[64, 64]`):
+ The feature sizes for the Rope positional encoding.
+ memory_attention_rope_k_sizes (`List[int]`, *optional*, defaults to `[16, 16]`):
+ The key feature sizes for the RoPE positional encoding in memory attention.
+ memory_attention_rope_dropout (`float`, *optional*, defaults to 0.1):
+ The dropout rate for the Rope positional encoding.
+ perceiver_resampler_num_latents (`int`, *optional*, defaults to 256):
+ The number of 1D latent tokens in the perceiver resampler.
+ perceiver_resampler_num_latents_2d (`int`, *optional*, defaults to 256):
+ The number of 2D latent tokens in the perceiver resampler.
+ perceiver_resampler_hidden_size (`int`, *optional*, defaults to 64):
+ The hidden size of the perceiver resampler.
+ perceiver_resampler_mlp_intermediate_size (`int`, *optional*, defaults to 256):
+ The intermediate size of the feedforward network in the perceiver resampler.
+ perceiver_resampler_num_attention_heads (`int`, *optional*, defaults to 1):
+ The number of attention heads in the perceiver resampler.
+ perceiver_resampler_attention_head_dim (`int`, *optional*, defaults to 64):
+ The dimension of each attention head in the perceiver resampler.
+ perceiver_resampler_num_layers (`int`, *optional*, defaults to 2):
+ The number of layers in the perceiver resampler.
+ perceiver_resampler_hidden_dropout (`float`, *optional*, defaults to 0.0):
+ The dropout rate for the hidden layers in the perceiver resampler.
+ perceiver_resampler_attention_dropout (`float`, *optional*, defaults to 0.0):
+ The dropout rate for the attention layers in the perceiver resampler.
+ memory_encoder_hidden_size (`int`, *optional*, defaults to 256):
+ Dimensionality of the memory encoder hidden states.
+ memory_encoder_output_channels (`int`, *optional*, defaults to 64):
+ The number of output channels for the memory encoder.
+ mask_downsampler_embed_dim (`int`, *optional*, defaults to 256):
+ The dimension of the mask downsampler embedding.
+ memory_fuser_intermediate_dim (`int`, *optional*, defaults to 1024):
+ The intermediate dimension of the memory fuser feedforward network.
+ mask_downsampler_kernel_size (`int`, *optional*, defaults to 3):
+ The kernel size for the mask downsampler.
+ mask_downsampler_stride (`int`, *optional*, defaults to 2):
+ The stride for the mask downsampler.
+ mask_downsampler_padding (`int`, *optional*, defaults to 1):
+ The padding for the mask downsampler.
+ mask_downsampler_total_stride (`int`, *optional*, defaults to 16):
+ The total stride for the mask downsampler.
+ mask_downsampler_hidden_act (`str`, *optional*, defaults to `"gelu"`):
+ The non-linear activation function in the mask downsampler.
+ memory_fuser_num_layers (`int`, *optional*, defaults to 2):
+ The number of layers in the memory fuser.
+ memory_fuser_embed_dim (`int`, *optional*, defaults to 256):
+ The dimension of the memory fuser embedding.
+ memory_fuser_kernel_size (`int`, *optional*, defaults to 7):
+ The kernel size for the memory fuser.
+ memory_fuser_padding (`int`, *optional*, defaults to 3):
+ The padding for the memory fuser.
+ memory_fuser_layer_scale_init_value (`float`, *optional*, defaults to 1e-06):
+ The initial value for the layer scale in the memory fuser.
+ memory_fuser_hidden_act (`str`, *optional*, defaults to `"gelu"`):
+ The non-linear activation function in the memory fuser.
+
+ Example:
+
+ ```python
+ >>> from transformers import (
+ ... EdgeTamVisionConfig,
+ ... EdgeTamVideoPromptEncoderConfig,
+ ... EdgeTamVideoMaskDecoderConfig,
+ ... EdgeTamVideoModel,
+ ... EdgeTamVideoConfig,
+ ... )
+
+ >>> # Initializing a EdgeTamVideoConfig with `"facebook/edgetam.1_hiera_tiny"` style configuration
+ >>> configuration = EdgeTamVideoConfig()
+
+ >>> # Initializing a EdgeTamVideoModel (with random weights) from the `"facebook/edgetam.1_hiera_tiny"` style configuration
+ >>> model = EdgeTamVideoModel(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+
+ >>> # We can also initialize a EdgeTamConfig from a EdgeTamVisionConfig, EdgeTamPromptEncoderConfig, and EdgeTamMaskDecoderConfig
+
+ >>> # Initializing EDGETAM vision encoder, memory attention, and memory encoder configurations
+ >>> vision_config = EdgeTamVisionConfig()
+ >>> prompt_encoder_config = EdgeTamVideoPromptEncoderConfig()
+ >>> mask_decoder_config = EdgeTamVideoMaskDecoderConfig()
+
+ >>> config = EdgeTamVideoConfig(vision_config, prompt_encoder_config, mask_decoder_config)
+ ```"""
+
+ model_type = "edgetam_video"
+ sub_configs = {
+ "vision_config": AutoConfig,
+ "prompt_encoder_config": EdgeTamVideoPromptEncoderConfig,
+ "mask_decoder_config": EdgeTamVideoMaskDecoderConfig,
+ }
+
+ vision_config: dict | PreTrainedConfig | None = None
+ prompt_encoder_config: dict | PreTrainedConfig | None = None
+ mask_decoder_config: dict | PreTrainedConfig | None = None
+ initializer_range: float = 0.02
+ num_maskmem: int = 7
+ image_size: int | list[int] | tuple[int, int] = 1024
+ sigmoid_scale_for_mem_enc: float = 20.0
+ sigmoid_bias_for_mem_enc: float = -10.0
+ enable_occlusion_spatial_embedding: bool = True
+ multimask_output_in_sam: bool = True
+ multimask_min_pt_num: int = 0
+ multimask_max_pt_num: int = 1
+ multimask_output_for_tracking: bool = True
+ max_object_pointers_in_encoder: int = 16
+ max_cond_frame_num: int = -1
+ enable_temporal_pos_encoding_for_object_pointers: bool = True
+
+ # memory attention
+ memory_attention_hidden_size: int = 256
+ memory_attention_num_layers: int = 2
+ memory_attention_num_attention_heads: int = 1
+ memory_attention_downsample_rate: int = 1
+ memory_attention_mlp_hidden_size: int = 2048
+ memory_attention_mlp_hidden_act: str = "relu"
+ memory_attention_dropout: float | int = 0.1
+ memory_attention_rope_theta: float | int = 10000
+ memory_attention_rope_feat_sizes: list | None = None
+ memory_attention_rope_k_sizes: list | None = None
+ memory_attention_rope_dropout: float | int = 0.1
+
+ # spatial perceiver resampler
+ perceiver_resampler_num_latents: int = 256
+ perceiver_resampler_num_latents_2d: int = 256
+ perceiver_resampler_hidden_size: int = 64
+ perceiver_resampler_mlp_intermediate_size: int = 256
+ perceiver_resampler_num_attention_heads: int = 1
+ perceiver_resampler_attention_head_dim: int = 64
+ perceiver_resampler_num_layers: int = 2
+ perceiver_resampler_hidden_dropout: float | int = 0.0
+ perceiver_resampler_attention_dropout: float | int = 0.0
+
+ # memory encoder
+ memory_encoder_hidden_size: int = 256
+ memory_encoder_output_channels: int = 64
+ mask_downsampler_embed_dim: int = 256
+ memory_fuser_intermediate_dim: int = 1024
+ mask_downsampler_kernel_size: int = 3
+ mask_downsampler_stride: int = 2
+ mask_downsampler_padding: int = 1
+ mask_downsampler_total_stride: int = 16
+ mask_downsampler_hidden_act: str = "gelu"
+ memory_fuser_num_layers: int = 2
+ memory_fuser_embed_dim: int = 256
+ memory_fuser_kernel_size: int = 7
+ memory_fuser_padding: int = 3
+ memory_fuser_layer_scale_init_value: float = 1e-6
+ memory_fuser_hidden_act: str = "gelu"
+
+ def __post_init__(self, **kwargs):
+ self.prompt_encoder_config = self.prompt_encoder_config if self.prompt_encoder_config is not None else {}
+ self.mask_decoder_config = self.mask_decoder_config if self.mask_decoder_config is not None else {}
+ self.memory_attention_rope_feat_sizes = (
+ [64, 64] if self.memory_attention_rope_feat_sizes is None else self.memory_attention_rope_feat_sizes
+ )
+ self.memory_attention_rope_k_sizes = (
+ [16, 16] if self.memory_attention_rope_k_sizes is None else self.memory_attention_rope_k_sizes
+ )
+
+ if isinstance(self.vision_config, dict):
+ self.vision_config["model_type"] = self.vision_config.get("model_type", "sam2_vision_model")
+ self.vision_config = CONFIG_MAPPING[self.vision_config["model_type"]](**self.vision_config)
+ elif self.vision_config is None:
+ self.vision_config = CONFIG_MAPPING["sam2_vision_model"]()
+
+ if isinstance(self.prompt_encoder_config, dict):
+ self.prompt_encoder_config = EdgeTamVideoPromptEncoderConfig(**self.prompt_encoder_config)
+ elif self.prompt_encoder_config is None:
+ self.prompt_encoder_config = EdgeTamVideoPromptEncoderConfig()
+
+ if isinstance(self.mask_decoder_config, dict):
+ self.mask_decoder_config = EdgeTamVideoMaskDecoderConfig(**self.mask_decoder_config)
+ elif self.mask_decoder_config is None:
+ self.mask_decoder_config = EdgeTamVideoMaskDecoderConfig()
+ super().__post_init__(**kwargs)
+
+
+class EdgeTamVideoLayerNorm(Sam2VideoLayerNorm):
+ pass
+
+
+class EdgeTamVideoMemoryFuserCXBlock(Sam2VideoMemoryFuserCXBlock):
+ pass
+
+
+class EdgeTamVideoVisionEncoderOutput(Sam2VideoVisionEncoderOutput):
+ pass
+
+
+class EdgeTamVideoVisionRotaryEmbedding(Sam2VideoVisionRotaryEmbedding):
+ def __init__(self, config: EdgeTamVideoConfig, end_x: int | None = None, end_y: int | None = None):
+ nn.Module.__init__()
+ self.dim = config.memory_attention_hidden_size // (
+ config.memory_attention_downsample_rate * config.memory_attention_num_attention_heads
+ )
+ # Ensure even dimension for proper axial splitting
+ if self.dim % 4 != 0:
+ raise ValueError("Dimension must be divisible by 4 for axial RoPE")
+ self.end_x, self.end_y = config.memory_attention_rope_feat_sizes if end_x is None else (end_x, end_y)
+ self.memory_attention_rope_theta = config.memory_attention_rope_theta
+
+ # directly register the cos and sin embeddings as we have a fixed feature shape
+ inv_freq = self.create_inv_freq()
+ self.register_buffer("rope_embeddings_cos", inv_freq.cos(), persistent=False)
+ self.register_buffer("rope_embeddings_sin", inv_freq.sin(), persistent=False)
+
+
+class EdgeTamVideoAttention(Sam2VideoAttention):
+ pass
+
+
+def apply_rotary_pos_emb_2d_self_attn(
+ q: torch.Tensor,
+ k: torch.Tensor,
+ cos: torch.Tensor,
+ sin: torch.Tensor,
+) -> tuple[torch.Tensor, torch.Tensor]:
+ """
+ Apply rotary position embedding to query and key tensors for self-attention.
+
+ Args:
+ q: Query tensor of shape (..., seq_len, head_dim)
+ k: Key tensor of shape (..., seq_len, head_dim)
+ cos: Cosine position embedding of shape (seq_len, head_dim)
+ sin: Sine position embedding of shape (seq_len, head_dim)
+
+ Returns:
+ Rotated (q, k) tensors
+ """
+ # Apply RoPE to queries
+ q_embed = q.float() # force upscale to float32 as in the original implementation
+ q_embed = (q_embed * cos) + (rotate_pairwise(q_embed) * sin)
+
+ # Apply RoPE to keys (same embeddings as queries for self-attention)
+ k_embed = k.float() # force upscale to float32 as in the original implementation
+ k_embed = (k_embed * cos) + (rotate_pairwise(k_embed) * sin)
+
+ return q_embed.type_as(q), k_embed.type_as(k)
+
+
+def apply_rotary_pos_emb_2d_cross_attn(
+ q: torch.Tensor,
+ k: torch.Tensor,
+ cos: torch.Tensor,
+ sin: torch.Tensor,
+ cos_k: torch.Tensor,
+ sin_k: torch.Tensor,
+ num_k_exclude_rope: int = 0,
+ repeat_freqs_k: int = 1,
+) -> tuple[torch.Tensor, torch.Tensor]:
+ """
+ Apply rotary position embedding to query and key tensors for cross-attention.
+
+ Args:
+ q: Query tensor of shape (..., seq_len, head_dim)
+ k: Key tensor of shape (..., seq_len, head_dim)
+ cos: Cosine position embedding of shape (seq_len, head_dim)
+ sin: Sine position embedding of shape (seq_len, head_dim)
+ cos_k: Cosine position embedding for keys of shape (seq_len, head_dim)
+ sin_k: Sine position embedding for keys of shape (seq_len, head_dim)
+ num_k_exclude_rope: Number of tokens at end of k to exclude from RoPE (e.g., object pointer tokens)
+ repeat_freqs_k: Frequency repetition for keys in cross-attention (e.g., for spatial memory tokens)
+
+ Returns:
+ Rotated (q, k) tensors
+ """
+ # Apply RoPE to queries (always straightforward)
+ q_embed = q.float()
+ q_embed = (q_embed * cos) + (rotate_pairwise(q_embed) * sin)
+
+ # Split keys: RoPE tokens and excluded tokens (e.g., object pointers)
+ num_total_k_tokens = k.shape[-2]
+ k_for_rope = k[..., : num_total_k_tokens - num_k_exclude_rope, :]
+ k_excluded = k[..., num_total_k_tokens - num_k_exclude_rope :, :]
+
+ # Early return if no keys need RoPE
+ if k_for_rope.shape[-2] == 0:
+ return q_embed.type_as(q), k_excluded
+
+ batch_size, num_heads, k_seq_len, channels_per_head = k_for_rope.shape
+
+ # Handle temporal/spatial token structure for memory
+ # Keys have temporal + spatial structure, only spatial tokens get RoPE
+ tokens_per_group = k_seq_len // repeat_freqs_k
+ spatial_tokens = cos_k.shape[-2]
+ temporal_tokens = tokens_per_group - spatial_tokens
+
+ # Reshape and separate temporal/spatial tokens
+ k_grouped = k_for_rope.view(batch_size, num_heads, repeat_freqs_k, tokens_per_group, channels_per_head)
+ k_temporal = k_grouped[..., :temporal_tokens, :].reshape(batch_size, num_heads, -1, channels_per_head)
+ k_spatial = k_grouped[..., temporal_tokens:, :].reshape(batch_size, num_heads, -1, channels_per_head)
+
+ # Only apply RoPE to spatial tokens
+ k_rope_input = k_spatial
+
+ # Prepare position embeddings for repeated groups
+ if repeat_freqs_k > 1:
+ cos_k = cos_k.repeat(1, 1, repeat_freqs_k, 1)
+ sin_k = sin_k.repeat(1, 1, repeat_freqs_k, 1)
+
+ # Apply RoPE to spatial tokens
+ k_spatial_embed = k_rope_input.float()
+ k_spatial_embed = (k_spatial_embed * cos_k) + (rotate_pairwise(k_spatial_embed) * sin_k)
+
+ # Reconstruct: temporal + spatial tokens back to original structure
+ k_spatial_reshaped = k_spatial_embed.view(batch_size, num_heads, repeat_freqs_k, -1, channels_per_head)
+ k_temporal_reshaped = k_temporal.view(batch_size, num_heads, repeat_freqs_k, -1, channels_per_head)
+ k_final = torch.cat([k_temporal_reshaped, k_spatial_reshaped], dim=3)
+ k_final = k_final.view(batch_size, num_heads, k_seq_len, channels_per_head)
+
+ # Combine RoPE-processed keys with excluded tokens
+ k_embed = torch.cat([k_final.type_as(k), k_excluded], dim=-2)
+ return q_embed.type_as(q), k_embed
+
+
+class EdgeTamVideoRoPESelfAttention(nn.Module):
+ """Self-attention with rotary position encoding."""
+
+ def __init__(self, config: EdgeTamVideoConfig):
+ super().__init__()
+ self.config = config
+ self.hidden_size = config.memory_attention_hidden_size
+ self.internal_dim = self.hidden_size // config.memory_attention_downsample_rate
+ self.num_attention_heads = config.memory_attention_num_attention_heads
+ self.head_dim = self.internal_dim // config.memory_attention_num_attention_heads
+ self.scaling = self.head_dim**-0.5
+ self.is_causal = False
+
+ self.q_proj = nn.Linear(self.hidden_size, self.internal_dim)
+ self.k_proj = nn.Linear(self.hidden_size, self.internal_dim)
+ self.v_proj = nn.Linear(self.hidden_size, self.internal_dim)
+ self.o_proj = nn.Linear(self.internal_dim, self.hidden_size)
+ self.dropout_p = config.memory_attention_rope_dropout
+
+ def forward(
+ self,
+ query: torch.Tensor,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor],
+ **kwargs: Unpack[FlashAttentionKwargs],
+ ) -> Tensor:
+ # Input projections
+ batch_size, point_batch_size = query.shape[:2]
+ new_shape = (batch_size * point_batch_size, -1, self.num_attention_heads, self.head_dim)
+
+ query = self.q_proj(query).view(*new_shape).transpose(1, 2)
+ key = self.k_proj(key).view(*new_shape).transpose(1, 2)
+ value = self.v_proj(value).view(*new_shape).transpose(1, 2)
+
+ cos, sin = position_embeddings
+ # Apply rotary position encoding for self-attention
+ query, key = apply_rotary_pos_emb_2d_self_attn(query, key, cos=cos, sin=sin)
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ query,
+ key,
+ value,
+ attention_mask=None,
+ dropout=0.0 if not self.training else self.dropout_p,
+ scaling=self.scaling,
+ is_causal=self.is_causal,
+ **kwargs,
+ )
+ attn_output = attn_output.reshape(
+ batch_size, point_batch_size, -1, self.num_attention_heads * self.head_dim
+ ).contiguous()
+ attn_output = self.o_proj(attn_output)
+ return attn_output, attn_weights
+
+
+class EdgeTamVideoRoPECrossAttention(nn.Module):
+ """Cross-attention with rotary position encoding."""
+
+ def __init__(self, config: EdgeTamVideoConfig, kv_in_dim: int):
+ super().__init__()
+ self.config = config
+ self.hidden_size = config.memory_attention_hidden_size
+ self.internal_dim = self.hidden_size // config.memory_attention_downsample_rate
+ self.num_attention_heads = config.memory_attention_num_attention_heads
+ self.head_dim = self.internal_dim // config.memory_attention_num_attention_heads
+ self.scaling = self.head_dim**-0.5
+ self.is_causal = False
+
+ self.kv_in_dim = kv_in_dim
+
+ self.q_proj = nn.Linear(self.hidden_size, self.internal_dim)
+ self.k_proj = nn.Linear(self.kv_in_dim, self.internal_dim)
+ self.v_proj = nn.Linear(self.kv_in_dim, self.internal_dim)
+ self.o_proj = nn.Linear(self.internal_dim, self.hidden_size)
+ self.dropout_p = config.memory_attention_rope_dropout
+
+ def forward(
+ self,
+ query: torch.Tensor,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor],
+ position_embeddings_k: tuple[torch.Tensor, torch.Tensor],
+ num_k_exclude_rope: int = 0,
+ rope_k_repeat: int = 0,
+ **kwargs: Unpack[FlashAttentionKwargs],
+ ) -> Tensor:
+ # Input projections
+ batch_size, point_batch_size = query.shape[:2]
+ new_shape = (batch_size * point_batch_size, -1, self.num_attention_heads, self.head_dim)
+
+ query = self.q_proj(query).view(*new_shape).transpose(1, 2)
+ key = self.k_proj(key).view(*new_shape).transpose(1, 2)
+ value = self.v_proj(value).view(*new_shape).transpose(1, 2)
+
+ cos, sin = position_embeddings
+ cos_k, sin_k = position_embeddings_k
+ # Apply rotary position encoding for cross-attention
+ query, key = apply_rotary_pos_emb_2d_cross_attn(
+ query,
+ key,
+ cos=cos,
+ sin=sin,
+ cos_k=cos_k,
+ sin_k=sin_k,
+ repeat_freqs_k=rope_k_repeat,
+ num_k_exclude_rope=num_k_exclude_rope,
+ )
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ query,
+ key,
+ value,
+ attention_mask=None,
+ dropout=0.0 if not self.training else self.dropout_p,
+ scaling=self.scaling,
+ is_causal=self.is_causal,
+ **kwargs,
+ )
+ attn_output = attn_output.reshape(
+ batch_size, point_batch_size, -1, self.num_attention_heads * self.head_dim
+ ).contiguous()
+ attn_output = self.o_proj(attn_output)
+ return attn_output, attn_weights
+
+
+class EdgeTamVideoTwoWayAttentionBlock(Sam2VideoTwoWayAttentionBlock):
+ pass
+
+
+class EdgeTamVideoPositionEmbeddingSine(Sam2VideoPositionEmbeddingSine):
+ # maxsize=2 because we need to cache the forward method for both memory encoder and perceiver resampler
+ @compile_compatible_method_lru_cache(maxsize=2)
+ def forward(self, **super_kwargs):
+ return super().forward(**super_kwargs)
+
+
+class EdgeTamVideoMemoryEncoder(Sam2VideoMemoryEncoder):
+ pass
+
+
+class EdgeTamVideoFeedForward(Sam2VideoFeedForward):
+ pass
+
+
+class EdgeTamVideoPreTrainedModel(Sam2VideoPreTrainedModel):
+ def _init_weights(self, module):
+ super()._init_weights()
+ if isinstance(module, EdgeTamVideoVisionRotaryEmbedding):
+ inv_freq = module.create_inv_freq()
+ init.copy_(module.rope_embeddings_cos, inv_freq.cos())
+ init.copy_(module.rope_embeddings_sin, inv_freq.sin())
+
+
+class EdgeTamVideoInferenceSession(Sam2VideoInferenceSession):
+ pass
+
+
+class EdgeTamVideoMemoryAttentionMLP(nn.Module):
+ def __init__(self, config: EdgeTamVideoConfig):
+ super().__init__()
+ self.config = config
+ self.hidden_size = config.memory_attention_hidden_size
+ self.intermediate_size = config.memory_attention_mlp_hidden_size
+ self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size)
+ self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size)
+ self.dropout = nn.Dropout(config.memory_attention_dropout)
+ self.act_fn = ACT2FN[config.memory_attention_mlp_hidden_act]
+
+ def forward(self, x):
+ return self.down_proj(self.dropout(self.act_fn(self.up_proj(x))))
+
+
+class EdgeTamVideoMemoryAttentionLayer(nn.Module):
+ def __init__(self, config: EdgeTamVideoConfig):
+ super().__init__()
+ hidden_size = config.memory_attention_hidden_size
+ self.self_attn = EdgeTamVideoRoPESelfAttention(config)
+ self.cross_attn_image = EdgeTamVideoRoPECrossAttention(config, kv_in_dim=64)
+
+ # MLP module
+ self.mlp = EdgeTamVideoMemoryAttentionMLP(config)
+
+ self.layer_norm1 = nn.LayerNorm(hidden_size)
+ self.layer_norm2 = nn.LayerNorm(hidden_size)
+ self.layer_norm3 = nn.LayerNorm(hidden_size)
+ self.dropout1 = nn.Dropout(config.memory_attention_dropout)
+ self.dropout2 = nn.Dropout(config.memory_attention_dropout)
+ self.dropout3 = nn.Dropout(config.memory_attention_dropout)
+
+ def forward(
+ self,
+ queries: Tensor,
+ keys: Tensor,
+ key_point_embedding: Tensor,
+ rope_position_embeddings: tuple[Tensor, Tensor],
+ rope_position_embeddings_k: tuple[Tensor, Tensor] | None = None,
+ num_k_exclude_rope: int = 0,
+ rope_k_repeat: int = 0,
+ ) -> torch.Tensor:
+ # Self-Attention
+ query = self.layer_norm1(queries)
+ query, _ = self.self_attn(query=query, key=query, value=query, position_embeddings=rope_position_embeddings)
+ queries = queries + self.dropout1(query)
+
+ # Cross-Attention
+ query = self.layer_norm2(queries)
+ query, _ = self.cross_attn_image(
+ query=query,
+ key=keys + key_point_embedding,
+ value=keys,
+ position_embeddings=rope_position_embeddings,
+ position_embeddings_k=rope_position_embeddings_k,
+ num_k_exclude_rope=num_k_exclude_rope,
+ rope_k_repeat=rope_k_repeat,
+ )
+ queries = queries + self.dropout2(query)
+ # MLP
+ query = self.layer_norm3(queries)
+ query = self.mlp(query)
+ queries = queries + self.dropout3(query)
+ return queries
+
+
+class EdgeTamVideoMemoryAttention(Sam2VideoMemoryAttention):
+ def __init__(self, config: EdgeTamVideoConfig):
+ super().__init__()
+ self.rotary_emb_k = EdgeTamVideoVisionRotaryEmbedding(
+ config, end_x=config.memory_attention_rope_k_sizes[0], end_y=config.memory_attention_rope_k_sizes[1]
+ )
+
+ def forward(
+ self,
+ current_vision_features: torch.Tensor,
+ memory: torch.Tensor,
+ current_vision_position_embeddings: Tensor | None = None,
+ memory_posision_embeddings: Tensor | None = None,
+ num_object_pointer_tokens: int = 0,
+ num_spatial_memory_tokens: int = -1,
+ ):
+ """
+ Args:
+ current_vision_features (`torch.FloatTensor`):
+ The current vision features used for self-attention.
+ memory (`torch.FloatTensor`):
+ The memory features used for cross-attention.
+ current_vision_position_embeddings (`torch.FloatTensor`, *optional*):
+ The position embeddings for the current vision features.
+ memory_posision_embeddings (`torch.FloatTensor`, *optional*):
+ The position embeddings for the memory features.
+ num_object_pointer_tokens (`int`, *optional*, defaults to 0):
+ The number of object pointer tokens.
+ """
+ output = current_vision_features
+ if current_vision_position_embeddings is not None:
+ output = output + 0.1 * current_vision_position_embeddings
+
+ # Convert to batch first
+ output = output.transpose(0, 1)
+ memory = memory.transpose(0, 1).unsqueeze(1)
+ memory_posision_embeddings = memory_posision_embeddings.transpose(0, 1).unsqueeze(1)
+ rope_position_embeddings = self.rotary_emb()
+ rope_position_embeddings_k = self.rotary_emb_k()
+ for layer in self.layers:
+ output = layer(
+ queries=output.unsqueeze(1) if output.ndim == 3 else output,
+ keys=memory,
+ key_point_embedding=memory_posision_embeddings,
+ rope_position_embeddings=rope_position_embeddings,
+ rope_position_embeddings_k=rope_position_embeddings_k,
+ num_k_exclude_rope=num_object_pointer_tokens,
+ rope_k_repeat=num_spatial_memory_tokens,
+ )
+
+ normed_output = self.layer_norm(output)
+
+ # Convert back to seq first
+ normed_output = normed_output.transpose(0, 1)
+
+ return normed_output
+
+
+class EdgeTamVideoPerceiverMLP(nn.Module):
+ def __init__(self, config: EdgeTamVideoConfig):
+ super().__init__()
+ self.hidden_size = config.perceiver_resampler_hidden_size
+ self.intermediate_size = config.perceiver_resampler_mlp_intermediate_size
+
+ self.layer_norm = nn.LayerNorm(self.hidden_size)
+ self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
+ self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False)
+ self.act_fn = nn.GELU()
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ hidden_states = self.layer_norm(hidden_states)
+ hidden_states = self.down_proj(self.act_fn(self.up_proj(hidden_states)))
+ return hidden_states
+
+
+class EdgeTamVideoPerceiverAttention(nn.Module):
+ def __init__(self, config: EdgeTamVideoConfig):
+ super().__init__()
+ self.config = config
+ self.hidden_size = config.perceiver_resampler_hidden_size
+ self.num_attention_heads = config.perceiver_resampler_num_attention_heads
+ self.head_dim = config.perceiver_resampler_attention_head_dim
+ self.attention_dropout = config.perceiver_resampler_attention_dropout
+
+ self.inner_dim = self.head_dim * self.num_attention_heads
+ self.scaling = self.head_dim**-0.5
+ self.is_causal = False
+
+ self.q_proj = nn.Linear(self.hidden_size, self.inner_dim, bias=False)
+ self.k_proj = nn.Linear(self.hidden_size, self.inner_dim, bias=False)
+ self.v_proj = nn.Linear(self.hidden_size, self.inner_dim, bias=False)
+ self.o_proj = nn.Linear(self.inner_dim, self.hidden_size, bias=False)
+
+ def forward(
+ self,
+ query: torch.Tensor,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ positional_encoding: torch.Tensor | None = None,
+ **kwargs,
+ ) -> torch.Tensor:
+ # Project queries, keys, and values
+ query = self.q_proj(query)
+ key = self.k_proj(key)
+ value = self.v_proj(value)
+
+ # Reshape for multi-head attention
+ batch_size, seq_len_q = query.shape[:2]
+ query = query.view(batch_size, seq_len_q, self.num_attention_heads, self.head_dim).transpose(1, 2)
+ seq_len_kv = key.shape[1]
+ key = key.view(batch_size, seq_len_kv, self.num_attention_heads, self.head_dim).transpose(1, 2)
+ value = value.view(batch_size, seq_len_kv, self.num_attention_heads, self.head_dim).transpose(1, 2)
+
+ # Add positional encoding if provided
+ if positional_encoding is not None:
+ pos_encoding = positional_encoding.view(
+ batch_size, seq_len_kv, self.num_attention_heads, self.head_dim
+ ).transpose(1, 2)
+ key = key + pos_encoding
+ value = value + pos_encoding
+
+ # Apply attention
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, _ = attention_interface(
+ self,
+ query,
+ key,
+ value,
+ attention_mask=None,
+ dropout=0.0 if not self.training else self.attention_dropout,
+ scaling=self.scaling,
+ is_causal=self.is_causal,
+ **kwargs,
+ )
+
+ # Reshape output
+ attn_output = attn_output.transpose(1, 2).contiguous().view(batch_size, seq_len_q, self.inner_dim)
+ return self.o_proj(attn_output)
+
+
+class EdgeTamVideoPerceiverEncoderLayer(nn.Module):
+ def __init__(self, config: EdgeTamVideoConfig):
+ super().__init__()
+
+ self.cross_attention = EdgeTamVideoPerceiverAttention(config)
+ self.mlp = EdgeTamVideoPerceiverMLP(config)
+ self.dropout = nn.Dropout(config.perceiver_resampler_hidden_dropout)
+
+ self.self_attention = EdgeTamVideoPerceiverAttention(config)
+ self.self_mlp = EdgeTamVideoPerceiverMLP(config)
+
+ # Layer norms moved from attention classes to here
+ self.layer_norm_input = nn.LayerNorm(config.perceiver_resampler_hidden_size)
+ self.layer_norm_latents = nn.LayerNorm(config.perceiver_resampler_hidden_size)
+ self.layer_norm_self = nn.LayerNorm(config.perceiver_resampler_hidden_size)
+
+ def forward(
+ self,
+ latents: torch.Tensor,
+ input_features: torch.Tensor,
+ positional_encoding: torch.Tensor | None = None,
+ ) -> torch.Tensor:
+ # Cross attention with layer norms
+ normalized_latents = self.layer_norm_latents(latents)
+ normalized_input = self.layer_norm_input(input_features)
+ cross_attention_output = self.cross_attention(
+ query=normalized_latents,
+ key=normalized_input,
+ value=normalized_input,
+ positional_encoding=positional_encoding,
+ )
+ latents = latents + self.dropout(cross_attention_output)
+
+ mlp_output = self.mlp(latents)
+ latents = latents + mlp_output
+
+ # Self attention with layer norm
+ normalized_latents_self = self.layer_norm_self(latents)
+ self_attention_output = self.self_attention(
+ query=normalized_latents_self, key=normalized_latents_self, value=normalized_latents_self
+ )
+ latents = latents + self_attention_output
+
+ self_mlp_output = self.self_mlp(latents)
+ latents = latents + self_mlp_output
+
+ return latents
+
+
+class EdgeTamVideoPerceiverResampler(nn.Module):
+ def __init__(self, config: EdgeTamVideoConfig):
+ super().__init__()
+ self.config = config
+ self.hidden_size = config.perceiver_resampler_hidden_size
+ self.num_latents_1d = config.perceiver_resampler_num_latents
+ self.num_latents_2d = config.perceiver_resampler_num_latents_2d
+ self.num_layers = config.perceiver_resampler_num_layers
+
+ if self.num_latents_1d > 0:
+ self.latents_1d = nn.Parameter(torch.randn(self.num_latents_1d, self.hidden_size))
+ if self.num_latents_2d > 0:
+ self.latents_2d = nn.Parameter(torch.randn(self.num_latents_2d, self.hidden_size))
+
+ self.positional_encoding = EdgeTamVideoPositionEmbeddingSine(
+ num_pos_feats=self.hidden_size // 2, normalize=True
+ )
+
+ self.layers = nn.ModuleList([EdgeTamVideoPerceiverEncoderLayer(config) for _ in range(self.num_layers)])
+
+ self.layer_norm = nn.LayerNorm(self.hidden_size)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ positional_encoding: torch.Tensor | None = None,
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
+ output_latents = []
+ output_positional_encodings = []
+
+ if self.num_latents_1d > 0:
+ latents_1d, pos_1d = self._forward_1d(hidden_states, positional_encoding)
+ output_latents.append(latents_1d)
+ output_positional_encodings.append(pos_1d)
+
+ if self.num_latents_2d > 0:
+ latents_2d, pos_2d = self._forward_2d(hidden_states)
+ output_latents.append(latents_2d)
+ output_positional_encodings.append(pos_2d)
+
+ combined_latents = torch.cat(output_latents, dim=1)
+
+ combined_positional_encoding = None
+ if positional_encoding is not None and output_positional_encodings:
+ combined_positional_encoding = torch.cat(output_positional_encodings, dim=1)
+
+ return combined_latents, combined_positional_encoding
+
+ def _forward_1d(
+ self,
+ hidden_states: torch.Tensor,
+ positional_encoding: torch.Tensor | None = None,
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
+ batch_size = hidden_states.shape[0]
+
+ latents = self.latents_1d.unsqueeze(0).expand(batch_size, -1, -1)
+ flattened_features = hidden_states.permute(0, 2, 3, 1).flatten(1, 2)
+
+ positional_features = None
+ if positional_encoding is not None:
+ positional_features = positional_encoding.permute(0, 2, 3, 1).flatten(1, 2)
+
+ for layer in self.layers:
+ latents = layer(latents, flattened_features, positional_features)
+
+ latents = self.layer_norm(latents)
+
+ output_positional_encoding = None
+ if positional_encoding is not None:
+ output_positional_encoding = torch.zeros_like(latents)
+
+ return latents, output_positional_encoding
+
+ def _forward_2d(self, hidden_states: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
+ batch_size, channels, height, width = hidden_states.shape
+
+ latents_2d = self.latents_2d.unsqueeze(0).expand(batch_size, -1, -1).view(-1, 1, channels)
+
+ num_windows_per_dim = int(math.sqrt(self.num_latents_2d))
+ window_size = height // num_windows_per_dim
+
+ windowed_input = hidden_states.permute(0, 2, 3, 1)
+ windowed_features, _ = window_partition(windowed_input, window_size)
+ windowed_features = windowed_features.flatten(1, 2)
+
+ for layer in self.layers:
+ latents_2d = layer(latents_2d, windowed_features, positional_encoding=None)
+
+ latents_2d = latents_2d.view(batch_size, num_windows_per_dim, num_windows_per_dim, channels).permute(
+ 0, 3, 1, 2
+ )
+
+ positional_encoding_2d = self.positional_encoding(latents_2d.shape, latents_2d.device, latents_2d.dtype).to(
+ dtype=hidden_states.dtype
+ )
+ positional_encoding_2d = positional_encoding_2d.permute(0, 2, 3, 1).flatten(1, 2)
+
+ latents_2d = latents_2d.permute(0, 2, 3, 1).flatten(1, 2)
+ latents_2d = self.layer_norm(latents_2d)
+
+ return latents_2d, positional_encoding_2d
+
+
+class EdgeTamVideoImageSegmentationOutput(Sam2VideoImageSegmentationOutput):
+ pass
+
+
+class EdgeTamVideoSegmentationOutput(Sam2VideoSegmentationOutput):
+ pass
+
+
+@auto_docstring
+class EdgeTamVideoModel(Sam2VideoModel):
+ _keys_to_ignore_on_load_unexpected = []
+ _can_record_outputs = {"mask_decoder_attentions": OutputRecorder(EdgeTamVideoTwoWayAttentionBlock, index=2)}
+
+ def __init__(self, config: EdgeTamVideoConfig):
+ super().__init__(config)
+ self.spatial_perceiver = EdgeTamVideoPerceiverResampler(config)
+
+ self.post_init()
+
+ def _build_memory_attention_inputs(
+ self,
+ temporal_positions_and_previous_outputs: list[tuple[int, dict]],
+ device: torch.device,
+ ) -> tuple[list[torch.Tensor], list[torch.Tensor]]:
+ """
+ Concatenate memory features and positional embeddings from previous frames.
+
+ Returns:
+ Tuple of (memories_to_concatenate, memory_positional_embeddings_to_concatenate).
+ """
+ memories_to_concatenate = []
+ memory_positional_embeddings_to_concatenate = []
+
+ for relative_temporal_offset, prev_output_data in temporal_positions_and_previous_outputs:
+ if prev_output_data is None:
+ continue # Skip if no output data for this temporal position (e.g., padding frames)
+
+ # Load memory features (potentially from CPU to GPU)
+ # Features are flattened: (Batch, Channels, H, W) -> (H*W, Batch, Channels)
+ memory_features = prev_output_data["maskmem_features"].to(device, non_blocking=True)
+ memories_to_concatenate.append(memory_features.permute(1, 0, 2))
+
+ # Spatial positional encoding (potentially from CPU to GPU)
+ spatial_memory_pos_embed = prev_output_data["maskmem_pos_enc"].to(device, non_blocking=True)
+ spatial_memory_pos_embed = spatial_memory_pos_embed.squeeze(1).permute(1, 0, 2)
+
+ # Add temporal positional encoding
+ # self.memory_temporal_positional_encoding shape: (NumMaskMem, 1, 1, MemDim)
+ combined_memory_pos_embed = (
+ spatial_memory_pos_embed + self.memory_temporal_positional_encoding[relative_temporal_offset - 1]
+ )
+ memory_positional_embeddings_to_concatenate.append(combined_memory_pos_embed)
+
+ return memories_to_concatenate, memory_positional_embeddings_to_concatenate
+
+ def _prepare_memory_conditioned_features(
+ self,
+ inference_session: EdgeTamVideoInferenceSession,
+ frame_idx: int,
+ obj_idx: int,
+ is_initial_conditioning_frame: bool,
+ current_vision_features: list[torch.Tensor],
+ current_vision_positional_embeddings: list[torch.Tensor],
+ num_total_frames: int,
+ track_in_reverse_time: bool = False,
+ streaming: bool = False,
+ ) -> torch.Tensor:
+ """
+ Fuse current frame's visual features with memory from previous frames for enhanced object tracking.
+
+ This method conditions the current frame's visual features on temporal memory from previous frames,
+ enabling consistent object tracking across video sequences. For initial conditioning frames, it uses
+ no-memory embeddings. For subsequent frames, it retrieves and integrates memory features from both
+ conditioning frames (user interactions) and non-conditioning frames (tracked results) via cross-attention.
+
+ Args:
+ inference_session (`EdgeTamVideoInferenceSession`):
+ The video inference session object.
+ frame_idx (`int`):
+ Index of the current frame being processed.
+ obj_idx (`int`):
+ Index of the object being processed.
+ is_initial_conditioning_frame (`bool`):
+ Whether this is an initial conditioning frame with user inputs (True) or a subsequent
+ tracking frame (False).
+ current_vision_features (`torch.Tensor`):
+ Highest-level vision features of shape `(seq_len, batch_size, channels)`.
+ current_vision_positional_embeddings (`torch.Tensor`):
+ Positional embedding tensors corresponding to the highest-level vision features.
+ num_total_frames (`int`):
+ Total number of frames in the video sequence.
+ track_in_reverse_time (`bool`, *optional*, defaults to `False`):
+ Whether tracking is performed in reverse temporal order.
+ streaming (`bool`, *optional*, defaults to `False`):
+ Whether this is streaming inference mode.
+
+ Returns:
+ `torch.Tensor`: Memory-conditioned feature tensor of shape `(batch_size, channels, height, width)`
+ suitable for input to the SAM decoder.
+ """
+ # Get dimensions from the highest-level (lowest-resolution) feature map
+ batch_size = current_vision_features.size(1)
+ num_channels = self.hidden_dim
+ height, width = self.backbone_feature_sizes[-1]
+ device = current_vision_features.device
+
+ # If memory is disabled (e.g., for single image SAM), return current features directly.
+ if self.num_maskmem == 0:
+ # Permute (SeqLen, Batch, Channels) -> (Batch, Channels, SeqLen) then view as (Batch, Channels, Height, Width)
+ # Assuming SeqLen = Height * Width for the last feature map
+ current_feature_map = current_vision_features.permute(1, 2, 0).view(
+ batch_size, num_channels, height, width
+ )
+ return current_feature_map
+
+ # Step 1: Handle initial conditioning frames
+ if is_initial_conditioning_frame:
+ # For initial conditioning frames, no prior memory is used directly in this block.
+ # If configured, directly add a learnable "no memory" embedding.
+ # current_vision_features has shape (SeqLen, Batch, Channels)
+ conditioned_feature_map_flat = current_vision_features + self.no_memory_embedding
+ # Reshape to (Batch, Channels, Height, Width)
+ conditioned_feature_map = conditioned_feature_map_flat.permute(1, 2, 0).view(
+ batch_size, num_channels, height, width
+ )
+ return conditioned_feature_map
+
+ # Step 2: Get memory frames and concatenate their features
+ temporal_positions_and_previous_outputs = self._gather_memory_frame_outputs(
+ inference_session, obj_idx, frame_idx, track_in_reverse_time
+ )
+
+ memories_to_concatenate, memory_positional_embeddings_to_concatenate = self._build_memory_attention_inputs(
+ temporal_positions_and_previous_outputs, device
+ )
+ num_spatial_memory_tokens = len(memories_to_concatenate)
+
+ # Step 3: Get and process object pointers
+ temporal_offsets, pointer_tokens, max_object_pointers_to_use = self._get_object_pointers(
+ inference_session, obj_idx, frame_idx, num_total_frames, device, track_in_reverse_time, streaming
+ )
+
+ num_object_pointer_tokens = 0
+ if pointer_tokens:
+ object_pointers, object_pointers_pos_embed = self._process_object_pointers(
+ temporal_offsets, pointer_tokens, max_object_pointers_to_use, batch_size, num_channels, device
+ )
+
+ if object_pointers is not None:
+ memories_to_concatenate.append(object_pointers)
+ memory_positional_embeddings_to_concatenate.append(object_pointers_pos_embed)
+ num_object_pointer_tokens = object_pointers.shape[0]
+
+ # Step 4: Concatenate all retrieved memories and their positional embeddings
+ combined_memory = torch.cat(memories_to_concatenate, dim=0)
+ combined_memory_positional_embeddings = torch.cat(memory_positional_embeddings_to_concatenate, dim=0)
+
+ # Step 5: Forward through the memory attention mechanism
+ conditioned_feature_map_flat = self.memory_attention(
+ current_vision_features=current_vision_features,
+ current_vision_position_embeddings=current_vision_positional_embeddings,
+ memory=combined_memory,
+ memory_posision_embeddings=combined_memory_positional_embeddings, # Corrected typo from API
+ num_object_pointer_tokens=num_object_pointer_tokens,
+ num_spatial_memory_tokens=num_spatial_memory_tokens,
+ )
+
+ # Reshape from (Batch, H*W, Channels) to (Batch, Channels, Height, Width)
+ conditioned_feature_map = (
+ conditioned_feature_map_flat.squeeze(1).permute(0, 2, 1).view(batch_size, num_channels, height, width)
+ )
+ return conditioned_feature_map
+
+ def _encode_new_memory(
+ self,
+ current_vision_feats: torch.Tensor,
+ pred_masks_high_res: torch.Tensor,
+ object_score_logits: torch.Tensor,
+ is_mask_from_pts: bool,
+ ) -> tuple[torch.Tensor, list[torch.Tensor]]:
+ """Encode the current image and its prediction into a memory feature."""
+ batch_size = current_vision_feats.size(1) # batch size on this frame
+ channels = self.hidden_dim
+ height, width = self.backbone_feature_sizes[-1] # top-level (lowest-resolution) feature size
+ # top-level feature, (HW)BC => BCHW
+ pix_feat = current_vision_feats.permute(1, 2, 0).view(batch_size, channels, height, width)
+ if is_mask_from_pts and not self.training:
+ # binarize the mask logits
+ mask_for_mem = (pred_masks_high_res > 0).to(pred_masks_high_res.dtype)
+ else:
+ # apply sigmoid on the raw mask logits to turn them into range (0, 1)
+ mask_for_mem = torch.sigmoid(pred_masks_high_res)
+ # apply scale and bias terms to the sigmoid probabilities
+ mask_for_mem = mask_for_mem * self.config.sigmoid_scale_for_mem_enc
+ mask_for_mem = mask_for_mem + self.config.sigmoid_bias_for_mem_enc
+
+ maskmem_features, maskmem_pos_enc = self.memory_encoder(
+ pix_feat,
+ mask_for_mem,
+ )
+ # add a no-object embedding to the spatial memory to indicate that the frame
+ # is predicted to be occluded (i.e. no object is appearing in the frame)
+ if self.occlusion_spatial_embedding_parameter is not None:
+ is_obj_appearing = (object_score_logits > 0).float()
+ maskmem_features += (1 - is_obj_appearing[..., None]) * self.occlusion_spatial_embedding_parameter[
+ ..., None, None
+ ].expand(*maskmem_features.shape)
+
+ maskmem_pos_enc = maskmem_pos_enc.to(pred_masks_high_res.dtype)
+ maskmem_features, maskmem_pos_enc = self.spatial_perceiver(maskmem_features, maskmem_pos_enc)
+ maskmem_features = maskmem_features.to(pred_masks_high_res.dtype)
+ maskmem_pos_enc = maskmem_pos_enc.to(pred_masks_high_res.dtype)
+
+ return maskmem_features, maskmem_pos_enc
+
+ def forward(
+ self,
+ inference_session: EdgeTamVideoInferenceSession,
+ frame_idx: int | None = None,
+ frame: torch.Tensor | None = None,
+ reverse: bool = False,
+ **kwargs,
+ ) -> EdgeTamVideoSegmentationOutput:
+ r"""
+ inference_session (`EdgeTamVideoInferenceSession`):
+ The video inference session object.
+ frame_idx (`int`, *optional*):
+ The index of the frame on which to run inference. No need to provide when inferring
+ on a new streamed frame.
+ frame (`torch.Tensor`, *optional*):
+ The frame to process. Provide when streaming.
+ reverse (`bool`, *optional*, defaults to `False`):
+ Whether to propagate in reverse.
+ """
+ if frame is not None:
+ frame_idx = inference_session.add_new_frame(frame, frame_idx)
+
+ if frame is not None and inference_session.get_obj_num() == 0:
+ raise ValueError("No objects are provided for tracking; please add inputs first.")
+
+ num_objects = inference_session.get_obj_num()
+ pred_masks_per_obj = [None] * num_objects
+ object_score_logits_per_obj = [None] * num_objects
+ # Note: We avoid batched inference here because per-object inputs (clicks/masks)
+ # can differ across objects.
+ for obj_idx in range(num_objects):
+ obj_id = inference_session.obj_idx_to_id(obj_idx)
+ has_new_inputs = obj_id in inference_session.obj_with_new_inputs
+ has_cond_output = frame_idx in inference_session.output_dict_per_obj[obj_idx]["cond_frame_outputs"]
+ # If this object has no new inputs and this frame already has a
+ # conditioning output, reuse the cached masks instead of recomputing.
+ if (not has_new_inputs) and has_cond_output:
+ pred_masks = inference_session.get_output(obj_idx, frame_idx, "pred_masks", is_conditioning_frame=True)
+ object_score_logits = inference_session.get_output(
+ obj_idx, frame_idx, "object_score_logits", is_conditioning_frame=True
+ )
+ is_init_cond_frame = True
+ else:
+ # Defaults when there are no new inputs
+ is_init_cond_frame = False
+ point_inputs = None
+ mask_inputs = None
+
+ if has_new_inputs:
+ is_init_cond_frame = frame_idx not in inference_session.frames_tracked_per_obj[obj_idx]
+ if is_init_cond_frame:
+ reverse = False
+ point_inputs = inference_session.point_inputs_per_obj[obj_idx].get(frame_idx, None)
+ mask_inputs = inference_session.mask_inputs_per_obj[obj_idx].get(frame_idx, None)
+ if point_inputs is not None or mask_inputs is not None:
+ inference_session.obj_with_new_inputs.remove(obj_id)
+
+ current_out = self._run_single_frame_inference(
+ inference_session=inference_session,
+ obj_idx=obj_idx,
+ frame_idx=frame_idx,
+ batch_size=1, # run on the slice of a single object
+ is_init_cond_frame=is_init_cond_frame,
+ point_inputs=point_inputs,
+ mask_inputs=mask_inputs,
+ reverse=reverse,
+ run_mem_encoder=True,
+ streaming=frame is not None,
+ )
+ inference_session.store_output(
+ obj_idx, frame_idx, output_value=current_out, is_conditioning_frame=is_init_cond_frame
+ )
+ pred_masks = current_out["pred_masks"]
+ object_score_logits = current_out["object_score_logits"]
+
+ pred_masks_per_obj[obj_idx] = pred_masks
+ object_score_logits_per_obj[obj_idx] = object_score_logits.squeeze(-1)
+ if not is_init_cond_frame:
+ # only for tracked frames, not for initial conditioning frames
+ inference_session.frames_tracked_per_obj[obj_idx][frame_idx] = {"reverse": reverse}
+
+ # Resize the output mask to the original video resolution (we directly use
+ # the mask scores on GPU for output to avoid any CPU conversion in between)
+ if len(pred_masks_per_obj) > 1:
+ all_pred_masks = torch.cat(pred_masks_per_obj, dim=0)
+ all_object_score_logits = torch.cat(object_score_logits_per_obj, dim=0)
+ else:
+ all_pred_masks = pred_masks_per_obj[0]
+ all_object_score_logits = object_score_logits_per_obj[0]
+
+ return EdgeTamVideoSegmentationOutput(
+ object_ids=inference_session.obj_ids.copy(),
+ pred_masks=all_pred_masks,
+ object_score_logits=all_object_score_logits,
+ frame_idx=frame_idx,
+ )
+
+ def _use_mask_as_output(
+ self,
+ backbone_features: torch.Tensor,
+ high_res_features: list[torch.Tensor],
+ mask_inputs: torch.Tensor,
+ ) -> EdgeTamVideoImageSegmentationOutput:
+ """
+ Directly turn binary `mask_inputs` into a output mask logits without using SAM.
+ (same input and output shapes as in forward above).
+ """
+ # Use -10/+20 as logits for neg/pos pixels (very close to 0/1 in prob after sigmoid).
+ out_scale, out_bias = 20.0, -10.0 # sigmoid(-10.0)=4.5398e-05
+ mask_inputs_float = mask_inputs.to(backbone_features[0].dtype)
+ high_res_masks = mask_inputs_float * out_scale + out_bias
+ low_res_masks = F.interpolate(
+ high_res_masks.float(),
+ size=(high_res_masks.size(-2) // 4, high_res_masks.size(-1) // 4),
+ align_corners=False,
+ mode="bilinear",
+ antialias=True, # use antialias for downsampling
+ ).to(backbone_features[0].dtype)
+ # a dummy IoU prediction of all 1's under mask input
+ iou_scores = mask_inputs.new_ones(mask_inputs.size(0), 1).to(backbone_features[0].dtype)
+ # produce an object pointer using the SAM decoder from the mask input
+ object_pointer = self._single_frame_forward(
+ input_masks=self.mask_downsample(mask_inputs_float.to(backbone_features[0].dtype)),
+ image_embeddings=high_res_features + [backbone_features],
+ ).object_pointer
+ # In this method, we are treating mask_input as output, e.g. using it directly to create spatial mem;
+ # Below, we follow the same design axiom to use mask_input to decide if obj appears or not instead of relying
+ # on the object_scores from the SAM decoder.
+ is_obj_appearing = torch.any(mask_inputs.flatten(1).float() > 0.0, dim=1)
+ is_obj_appearing = is_obj_appearing[..., None]
+ lambda_is_obj_appearing = is_obj_appearing.to(backbone_features[0].dtype)
+ object_score_logits = out_scale * lambda_is_obj_appearing + out_bias
+ object_pointer = lambda_is_obj_appearing * object_pointer
+ object_pointer = object_pointer + (1 - lambda_is_obj_appearing) * self.no_object_pointer
+ return EdgeTamVideoImageSegmentationOutput(
+ iou_scores=iou_scores,
+ pred_masks=low_res_masks,
+ high_res_masks=high_res_masks,
+ object_pointer=object_pointer,
+ object_score_logits=object_score_logits,
+ image_embeddings=high_res_features + [backbone_features],
+ )
+
+ def _run_single_frame_inference(
+ self,
+ inference_session: EdgeTamVideoInferenceSession,
+ frame_idx: int,
+ obj_idx: int,
+ batch_size: int,
+ is_init_cond_frame: bool,
+ point_inputs: torch.Tensor | None,
+ mask_inputs: torch.Tensor | None,
+ reverse: bool,
+ run_mem_encoder: bool,
+ prev_sam_mask_logits: torch.Tensor | None = None,
+ streaming: bool = False,
+ ) -> dict[str, Any]:
+ """
+ Perform a single tracking step for video object segmentation.
+
+ Args:
+ inference_session (`EdgeTamVideoInferenceSession`):
+ The video inference session object.
+ frame_idx (`int`):
+ Index of the current frame.
+ obj_idx (`int`):
+ Index of the current object.
+ batch_size (`int`):
+ Batch size of the current frame.
+ is_init_cond_frame (`bool`):
+ Whether this is an initial conditioning frame with user inputs.
+ point_inputs (`dict`, *optional*):
+ Point prompt inputs for the current frame.
+ mask_inputs (`torch.Tensor`, *optional*):
+ Mask prompt inputs for the current frame.
+ reverse (`bool`, *optional*, defaults to `False`):
+ Whether to track in reverse time order.
+ run_mem_encoder (`bool`, *optional*, defaults to `True`):
+ Whether to run the memory encoder on predicted masks.
+ prev_sam_mask_logits (`torch.Tensor`, *optional*):
+ Previously predicted SAM mask logits that can be fed with new clicks.
+ streaming (`bool`, *optional*, defaults to `False`):
+ Whether this is streaming inference.
+
+ Returns:
+ `dict`: Dictionary containing the tracking results for the current frame, including:
+ - pred_masks: Predicted low-resolution masks.
+ - object_pointer: Object pointer for memory.
+ - object_score_logits: Object score logits (inference only).
+ - maskmem_features: Memory features for future frames.
+ - maskmem_pos_enc: Memory positional encodings.
+ """
+ # Retrieve correct image features
+ current_vision_feats, current_vision_pos_embeds = self._prepare_vision_features(
+ inference_session, frame_idx, batch_size
+ )
+ # point and mask should not appear as input simultaneously on the same frame
+ if point_inputs is not None and mask_inputs is not None:
+ raise ValueError(
+ "point_inputs and mask_inputs should not appear as input simultaneously on the same frame"
+ )
+ # High-resolution feature maps for the SAM head, reshape (HW)BC => BCHW
+ if len(current_vision_feats) > 1:
+ high_res_features = [
+ x.permute(1, 2, 0).view(x.size(1), x.size(2), *s)
+ for x, s in zip(current_vision_feats[:-1], self.backbone_feature_sizes[:-1])
+ ]
+ else:
+ high_res_features = None
+ if mask_inputs is not None:
+ # We directly output the mask input (see it as a GT mask) without using a SAM prompt encoder + mask decoder.
+ pix_feat = current_vision_feats[-1].permute(1, 2, 0)
+ pix_feat = pix_feat.view(-1, self.hidden_dim, *self.backbone_feature_sizes[-1])
+ sam_outputs = self._use_mask_as_output(pix_feat, high_res_features, mask_inputs)
+ else:
+ # fused the visual feature with previous memory features in the memory bank
+ pix_feat = self._prepare_memory_conditioned_features(
+ inference_session=inference_session,
+ frame_idx=frame_idx,
+ obj_idx=obj_idx,
+ is_initial_conditioning_frame=is_init_cond_frame,
+ current_vision_features=current_vision_feats[-1],
+ current_vision_positional_embeddings=current_vision_pos_embeds[-1],
+ num_total_frames=inference_session.num_frames,
+ track_in_reverse_time=reverse,
+ streaming=streaming,
+ )
+ # apply SAM-style segmentation head
+ # here we might feed previously predicted low-res SAM mask logits into the SAM mask decoder,
+ # e.g. in demo where such logits come from earlier interaction instead of correction sampling
+ # (in this case, any `mask_inputs` shouldn't reach here as they are sent to _use_mask_as_output instead)
+ if prev_sam_mask_logits is not None:
+ mask_inputs = prev_sam_mask_logits
+ multimask_output = self._use_multimask(is_init_cond_frame, point_inputs)
+ sam_outputs = self._single_frame_forward(
+ pixel_values=None, # Vision features already computed
+ input_points=point_inputs["point_coords"] if point_inputs is not None else None,
+ input_labels=point_inputs["point_labels"] if point_inputs is not None else None,
+ input_masks=mask_inputs,
+ image_embeddings=high_res_features + [pix_feat],
+ multimask_output=multimask_output,
+ )
+
+ # Finally run the memory encoder on the predicted mask to encode
+ # it into a new memory feature (which will be used to condition vision features in future frames)
+ maskmem_features = None
+ maskmem_pos_enc = None
+ if run_mem_encoder and self.num_maskmem > 0:
+ maskmem_features, maskmem_pos_enc = self._encode_new_memory(
+ current_vision_feats=current_vision_feats[-1],
+ pred_masks_high_res=sam_outputs.high_res_masks,
+ object_score_logits=sam_outputs.object_score_logits,
+ is_mask_from_pts=(point_inputs is not None or mask_inputs is not None),
+ )
+
+ current_out = {
+ "pred_masks": sam_outputs.pred_masks,
+ "object_pointer": sam_outputs.object_pointer,
+ "maskmem_features": maskmem_features if maskmem_features is not None else None,
+ "maskmem_pos_enc": maskmem_pos_enc,
+ }
+ if not self.training:
+ current_out["object_score_logits"] = sam_outputs.object_score_logits
+
+ return current_out
+
+ def _batch_encode_memories(self):
+ raise NotImplementedError("Batch memory encoding is not implemented for EdgeTamVideo yet.")
+ # Todo, implement batch memory encoding for edgetam video
+
+
+__all__ = [
+ "EdgeTamVideoMaskDecoderConfig",
+ "EdgeTamVideoPromptEncoderConfig",
+ "EdgeTamVideoConfig",
+ "EdgeTamVideoModel",
+ "EdgeTamVideoInferenceSession",
+ "EdgeTamVideoPreTrainedModel",
+]
diff --git a/third_party/transformers/src/transformers/models/encoder_decoder/__init__.py b/third_party/transformers/src/transformers/models/encoder_decoder/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..b1cde1442a13b526aa55a919fbc1462fd52459b3
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/encoder_decoder/__init__.py
@@ -0,0 +1,27 @@
+# Copyright 2024 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_encoder_decoder import *
+ from .modeling_encoder_decoder import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/encoder_decoder/configuration_encoder_decoder.py b/third_party/transformers/src/transformers/models/encoder_decoder/configuration_encoder_decoder.py
new file mode 100644
index 0000000000000000000000000000000000000000..c197ecb70b62c8e58cda72fbfa2a7d211725beaf
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/encoder_decoder/configuration_encoder_decoder.py
@@ -0,0 +1,102 @@
+# Copyright 2020 The HuggingFace Inc. team.
+# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...utils import auto_docstring, logging
+from ..auto import AutoConfig
+
+
+logger = logging.get_logger(__name__)
+
+
+@auto_docstring(checkpoint="")
+@strict
+class EncoderDecoderConfig(PreTrainedConfig):
+ r"""
+ Examples:
+
+ ```python
+ >>> from transformers import BertConfig, EncoderDecoderConfig, EncoderDecoderModel
+
+ >>> # Initializing a BERT google-bert/bert-base-uncased style configuration
+ >>> config_encoder = BertConfig()
+ >>> config_decoder = BertConfig()
+
+ >>> config = EncoderDecoderConfig.from_encoder_decoder_configs(config_encoder, config_decoder)
+
+ >>> # Initializing a Bert2Bert model (with random weights) from the google-bert/bert-base-uncased style configurations
+ >>> model = EncoderDecoderModel(config=config)
+
+ >>> # Accessing the model configuration
+ >>> config_encoder = model.config.encoder
+ >>> config_decoder = model.config.decoder
+ >>> # set decoder config to causal lm
+ >>> config_decoder.is_decoder = True
+ >>> config_decoder.add_cross_attention = True
+
+ >>> # Saving the model, including its configuration
+ >>> model.save_pretrained("my-model")
+
+ >>> # loading model and config from pretrained folder
+ >>> encoder_decoder_config = EncoderDecoderConfig.from_pretrained("my-model")
+ >>> model = EncoderDecoderModel.from_pretrained("my-model", config=encoder_decoder_config)
+ ```"""
+
+ model_type = "encoder-decoder"
+ sub_configs = {"encoder": AutoConfig, "decoder": AutoConfig}
+ has_no_defaults_at_init = True
+
+ pad_token_id: int | None = None
+ decoder_start_token_id: int | None = None
+ is_encoder_decoder: bool | None = True
+
+ def __post_init__(self, **kwargs):
+ if "encoder" not in kwargs or "decoder" not in kwargs:
+ raise ValueError(
+ f"A configuration of type {self.model_type} cannot be instantiated because not both `encoder` and"
+ f" `decoder` sub-configurations are passed, but only {kwargs}"
+ )
+
+ encoder_config = kwargs.pop("encoder")
+ encoder_model_type = encoder_config.pop("model_type")
+ decoder_config = kwargs.pop("decoder")
+ decoder_model_type = decoder_config.pop("model_type")
+
+ self.encoder = AutoConfig.for_model(encoder_model_type, **encoder_config)
+ self.decoder = AutoConfig.for_model(decoder_model_type, **decoder_config)
+ super().__post_init__(**kwargs)
+
+ @classmethod
+ def from_encoder_decoder_configs(
+ cls, encoder_config: PreTrainedConfig, decoder_config: PreTrainedConfig, **kwargs
+ ) -> PreTrainedConfig:
+ r"""
+ Instantiate a [`EncoderDecoderConfig`] (or a derived class) from a pre-trained encoder model configuration and
+ decoder model configuration.
+
+ Returns:
+ [`EncoderDecoderConfig`]: An instance of a configuration object
+ """
+ logger.info("Set `config.is_decoder=True` and `config.add_cross_attention=True` for decoder_config")
+ decoder_config.is_decoder = True
+ decoder_config.add_cross_attention = True
+
+ return cls(encoder=encoder_config.to_dict(), decoder=decoder_config.to_dict(), **kwargs)
+
+
+__all__ = ["EncoderDecoderConfig"]
diff --git a/third_party/transformers/src/transformers/models/encoder_decoder/modeling_encoder_decoder.py b/third_party/transformers/src/transformers/models/encoder_decoder/modeling_encoder_decoder.py
new file mode 100644
index 0000000000000000000000000000000000000000..f634f89ab89f94fda3adc870db542966207aef8c
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/encoder_decoder/modeling_encoder_decoder.py
@@ -0,0 +1,471 @@
+# Copyright 2018 The HuggingFace Inc. team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Classes to support Encoder-Decoder architectures"""
+
+import inspect
+import warnings
+
+import torch
+from torch import nn
+from torch.nn import CrossEntropyLoss
+
+from ...cache_utils import Cache
+from ...configuration_utils import PreTrainedConfig
+from ...generation import GenerationMixin
+from ...modeling_outputs import BaseModelOutput, Seq2SeqLMOutput
+from ...modeling_utils import PreTrainedModel
+from ...utils import auto_docstring, logging
+from ...utils.generic import can_return_tuple
+from ..auto.configuration_auto import AutoConfig
+from ..auto.modeling_auto import AutoModel, AutoModelForCausalLM
+from .configuration_encoder_decoder import EncoderDecoderConfig
+
+
+logger = logging.get_logger(__name__)
+
+
+DEPRECATION_WARNING = (
+ "Version v4.12.0 introduces a better way to train encoder-decoder models by computing the loss inside the"
+ " encoder-decoder framework rather than in the decoder itself. You may observe training discrepancies if"
+ " fine-tuning a model trained with versions anterior to 4.12.0. The decoder_input_ids are now created based on the"
+ " labels, no need to pass them yourself anymore."
+)
+
+
+def shift_tokens_right(input_ids: torch.Tensor, pad_token_id: int, decoder_start_token_id: int):
+ """
+ Shift input ids one token to the right.
+ """
+ shifted_input_ids = input_ids.new_zeros(input_ids.shape)
+ shifted_input_ids[:, 1:] = input_ids[:, :-1].clone()
+ if decoder_start_token_id is None:
+ raise ValueError("Make sure to set the decoder_start_token_id attribute of the model's configuration.")
+ shifted_input_ids[:, 0] = decoder_start_token_id
+
+ if pad_token_id is None:
+ raise ValueError("Make sure to set the pad_token_id attribute of the model's configuration.")
+ # replace possible -100 values in labels by `pad_token_id`
+ shifted_input_ids.masked_fill_(shifted_input_ids == -100, pad_token_id)
+
+ return shifted_input_ids
+
+
+@auto_docstring
+class EncoderDecoderModel(PreTrainedModel, GenerationMixin):
+ r"""
+ [`EncoderDecoderModel`] is a generic model class that will be instantiated as a transformer architecture with one
+ of the base model classes of the library as encoder and another one as decoder when created with the
+ :meth*~transformers.AutoModel.from_pretrained* class method for the encoder and
+ :meth*~transformers.AutoModelForCausalLM.from_pretrained* class method for the decoder.
+ """
+
+ config: EncoderDecoderConfig
+ base_model_prefix = "encoder_decoder"
+ main_input_name = "input_ids"
+ supports_gradient_checkpointing = True
+ _supports_flash_attn = True
+ _supports_sdpa = True
+
+ def __init__(
+ self,
+ config: PreTrainedConfig | None = None,
+ encoder: PreTrainedModel | None = None,
+ decoder: PreTrainedModel | None = None,
+ ):
+ r"""
+ encoder (`PreTrainedModel`, *optional*):
+ The encoder model to use.
+ decoder (`PreTrainedModel`, *optional*):
+ The decoder model to use.
+ """
+ if config is None and (encoder is None or decoder is None):
+ raise ValueError("Either a configuration or an encoder and a decoder has to be provided.")
+ if config is None:
+ config = EncoderDecoderConfig.from_encoder_decoder_configs(encoder.config, decoder.config)
+ else:
+ if not isinstance(config, self.config_class):
+ raise ValueError(f"Config: {config} has to be of type {self.config_class}")
+
+ if getattr(config.decoder, "cross_attention_hidden_size", None) is not None:
+ if config.decoder.cross_attention_hidden_size != config.encoder.hidden_size:
+ raise ValueError(
+ "If `cross_attention_hidden_size` is specified in the decoder's configuration, it has to be equal"
+ f" to the encoder's `hidden_size`. Got {config.decoder.cross_attention_hidden_size} for"
+ f" `config.decoder.cross_attention_hidden_size` and {config.encoder.hidden_size} for"
+ " `config.encoder.hidden_size`."
+ )
+
+ # initialize with config
+ super().__init__(config)
+
+ if encoder is None:
+ from ..auto.modeling_auto import AutoModel
+
+ encoder = AutoModel.from_config(config.encoder)
+
+ if decoder is None:
+ from ..auto.modeling_auto import AutoModelForCausalLM
+
+ decoder = AutoModelForCausalLM.from_config(config.decoder)
+
+ self.encoder = encoder
+ self.decoder = decoder
+
+ if self.encoder.config.to_dict() != self.config.encoder.to_dict():
+ logger.warning(
+ f"Config of the encoder: {self.encoder.__class__} is overwritten by shared encoder config:"
+ f" {self.config.encoder}"
+ )
+ if self.decoder.config.to_dict() != self.config.decoder.to_dict():
+ logger.warning(
+ f"Config of the decoder: {self.decoder.__class__} is overwritten by shared decoder config:"
+ f" {self.config.decoder}"
+ )
+
+ # make sure that the individual model's config refers to the shared config
+ # so that the updates to the config will be synced
+ # update `_attn_implementation` because the attn is set in a deepcopied config within PreTrainedModel
+ self.config.encoder._attn_implementation = self.encoder.config._attn_implementation
+ self.config.decoder._attn_implementation = self.decoder.config._attn_implementation
+ self.encoder.config = self.config.encoder
+ self.decoder.config = self.config.decoder
+
+ # encoder outputs might need to be projected to different dimension for decoder
+ if (
+ self.encoder.config.hidden_size != self.decoder.config.hidden_size
+ and getattr(self.decoder.config, "cross_attention_hidden_size", None) is None
+ ):
+ self.enc_to_dec_proj = nn.Linear(self.encoder.config.hidden_size, self.decoder.config.hidden_size)
+
+ if self.encoder.get_output_embeddings() is not None:
+ raise ValueError(
+ f"The encoder {self.encoder} should not have a LM Head. Please use a model without LM Head"
+ )
+
+ decoder_signature = set(inspect.signature(self.decoder.forward).parameters.keys())
+ if "encoder_hidden_states" not in decoder_signature:
+ raise ValueError(
+ "The selected decoder is not prepared for the encoder hidden states to be passed. Please see the "
+ "following discussion on GitHub: https://github.com/huggingface/transformers/issues/23350"
+ )
+
+ self.post_init()
+
+ @torch.no_grad()
+ def _init_weights(self, module):
+ if module in self.encoder.modules():
+ self.encoder._init_weights(module)
+ elif module in self.decoder.modules():
+ self.decoder._init_weights(module)
+
+ def get_input_embeddings(self):
+ return self.encoder.get_input_embeddings()
+
+ def get_output_embeddings(self):
+ return self.decoder.get_output_embeddings()
+
+ def set_output_embeddings(self, new_embeddings):
+ return self.decoder.set_output_embeddings(new_embeddings)
+
+ @classmethod
+ def from_encoder_decoder_pretrained(
+ cls,
+ encoder_pretrained_model_name_or_path: str | None = None,
+ decoder_pretrained_model_name_or_path: str | None = None,
+ *model_args,
+ **kwargs,
+ ) -> PreTrainedModel:
+ r"""
+ Instantiate an encoder and a decoder from one or two base classes of the library from pretrained model
+ checkpoints.
+
+
+ The model is set in evaluation mode by default using `model.eval()` (Dropout modules are deactivated). To train
+ the model, you need to first set it back in training mode with `model.train()`.
+
+ Params:
+ encoder_pretrained_model_name_or_path (`str`, *optional*):
+ Information necessary to initiate the encoder. Can be either:
+
+ - A string, the *model id* of a pretrained model hosted inside a model repo on huggingface.co.
+ - A path to a *directory* containing model weights saved using
+ [`~PreTrainedModel.save_pretrained`], e.g., `./my_model_directory/`.
+
+ decoder_pretrained_model_name_or_path (`str`, *optional*, defaults to `None`):
+ Information necessary to initiate the decoder. Can be either:
+
+ - A string, the *model id* of a pretrained model hosted inside a model repo on huggingface.co.
+ - A path to a *directory* containing model weights saved using
+ [`~PreTrainedModel.save_pretrained`], e.g., `./my_model_directory/`.
+
+ model_args (remaining positional arguments, *optional*):
+ All remaining positional arguments will be passed to the underlying model's `__init__` method.
+
+ kwargs (remaining dictionary of keyword arguments, *optional*):
+ Can be used to update the configuration object (after it being loaded) and initiate the model (e.g.,
+ `output_attentions=True`).
+
+ - To update the encoder configuration, use the prefix *encoder_* for each configuration parameter.
+ - To update the decoder configuration, use the prefix *decoder_* for each configuration parameter.
+ - To update the parent model configuration, do not use a prefix for each configuration parameter.
+
+ Behaves differently depending on whether a `config` is provided or automatically loaded.
+
+ Example:
+
+ ```python
+ >>> from transformers import EncoderDecoderModel
+
+ >>> # initialize a bert2bert from two pretrained BERT models. Note that the cross-attention layers will be randomly initialized
+ >>> model = EncoderDecoderModel.from_encoder_decoder_pretrained("google-bert/bert-base-uncased", "google-bert/bert-base-uncased")
+ >>> # saving model after fine-tuning
+ >>> model.save_pretrained("./bert2bert")
+ >>> # load fine-tuned model
+ >>> model = EncoderDecoderModel.from_pretrained("./bert2bert")
+ ```"""
+
+ kwargs_encoder = {
+ argument[len("encoder_") :]: value for argument, value in kwargs.items() if argument.startswith("encoder_")
+ }
+
+ kwargs_decoder = {
+ argument[len("decoder_") :]: value for argument, value in kwargs.items() if argument.startswith("decoder_")
+ }
+
+ # remove encoder, decoder kwargs from kwargs
+ for key in kwargs_encoder:
+ del kwargs["encoder_" + key]
+ for key in kwargs_decoder:
+ del kwargs["decoder_" + key]
+
+ # Load and initialize the encoder and decoder
+ # The distinction between encoder and decoder at the model level is made
+ # by the value of the flag `is_decoder` that we need to set correctly.
+ encoder = kwargs_encoder.pop("model", None)
+ if encoder is None:
+ if encoder_pretrained_model_name_or_path is None:
+ raise ValueError(
+ "If `encoder_model` is not defined as an argument, a `encoder_pretrained_model_name_or_path` has "
+ "to be defined."
+ )
+
+ if "config" not in kwargs_encoder:
+ encoder_config, kwargs_encoder = AutoConfig.from_pretrained(
+ encoder_pretrained_model_name_or_path, **kwargs_encoder, return_unused_kwargs=True
+ )
+
+ if getattr(encoder_config, "is_decoder", False) or getattr(
+ encoder_config, "add_cross_attention", False
+ ):
+ logger.info(
+ f"Initializing {encoder_pretrained_model_name_or_path} as a encoder model "
+ "from a decoder model. Cross-attention and causal mask are disabled."
+ )
+ encoder_config.is_decoder = False
+ encoder_config.add_cross_attention = False
+
+ kwargs_encoder["config"] = encoder_config
+
+ encoder = AutoModel.from_pretrained(encoder_pretrained_model_name_or_path, *model_args, **kwargs_encoder)
+
+ decoder = kwargs_decoder.pop("model", None)
+ if decoder is None:
+ if decoder_pretrained_model_name_or_path is None:
+ raise ValueError(
+ "If `decoder_model` is not defined as an argument, a `decoder_pretrained_model_name_or_path` has "
+ "to be defined."
+ )
+
+ if "config" not in kwargs_decoder:
+ decoder_config, kwargs_decoder = AutoConfig.from_pretrained(
+ decoder_pretrained_model_name_or_path, **kwargs_decoder, return_unused_kwargs=True
+ )
+ else:
+ decoder_config = kwargs_decoder["config"]
+
+ if (
+ getattr(decoder_config, "is_decoder", None) is False
+ or getattr(decoder_config, "add_cross_attention", None) is False
+ ):
+ logger.info(
+ f"Initializing {decoder_pretrained_model_name_or_path} as a decoder model. Cross attention"
+ f" layers are added to {decoder_pretrained_model_name_or_path} and randomly initialized if"
+ f" {decoder_pretrained_model_name_or_path}'s architecture allows for cross attention layers."
+ )
+ decoder_config.is_decoder = True
+ decoder_config.add_cross_attention = True
+
+ kwargs_decoder["config"] = decoder_config
+ decoder = AutoModelForCausalLM.from_pretrained(decoder_pretrained_model_name_or_path, **kwargs_decoder)
+
+ # instantiate config with corresponding kwargs
+ config = EncoderDecoderConfig.from_encoder_decoder_configs(encoder.config, decoder.config, **kwargs)
+ return cls(encoder=encoder, decoder=decoder, config=config)
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.FloatTensor | None = None,
+ decoder_input_ids: torch.LongTensor | None = None,
+ decoder_attention_mask: torch.BoolTensor | None = None,
+ encoder_outputs: tuple[torch.FloatTensor] | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ decoder_inputs_embeds: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ **kwargs,
+ ) -> tuple | Seq2SeqLMOutput:
+ r"""
+ decoder_input_ids (`torch.LongTensor` of shape `(batch_size, target_sequence_length)`, *optional*):
+ Indices of decoder input sequence tokens in the vocabulary.
+
+ Indices can be obtained using [`PreTrainedTokenizer`]. See [`PreTrainedTokenizer.encode`] and
+ [`PreTrainedTokenizer.__call__`] for details.
+
+ [What are input IDs?](../glossary#input-ids)
+
+ If `past_key_values` is used, optionally only the last `decoder_input_ids` have to be input (see
+ `past_key_values`).
+
+ For training, `decoder_input_ids` are automatically created by the model by shifting the `labels` to the
+ right, replacing -100 by the `pad_token_id` and prepending them with the `decoder_start_token_id`.
+ decoder_attention_mask (`torch.BoolTensor` of shape `(batch_size, target_sequence_length)`, *optional*):
+ Default behavior: generate a tensor that ignores pad tokens in `decoder_input_ids`. Causal mask will also
+ be used by default.
+ decoder_inputs_embeds (`torch.FloatTensor` of shape `(batch_size, target_sequence_length, hidden_size)`, *optional*):
+ Optionally, instead of passing `decoder_input_ids` you can choose to directly pass an embedded
+ representation. This is useful if you want more control over how to convert `decoder_input_ids` indices
+ into associated vectors than the model's internal embedding lookup matrix.
+ labels (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*):
+ Labels for computing the masked language modeling loss for the decoder. Indices should be in `[-100, 0,
+ ..., config.vocab_size]` (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]`
+
+ Examples:
+
+ ```python
+ >>> from transformers import EncoderDecoderModel, BertTokenizer
+ >>> import torch
+
+ >>> tokenizer = BertTokenizer.from_pretrained("google-bert/bert-base-uncased")
+ >>> model = EncoderDecoderModel.from_encoder_decoder_pretrained(
+ ... "google-bert/bert-base-uncased", "google-bert/bert-base-uncased"
+ ... ) # initialize Bert2Bert from pre-trained checkpoints
+
+ >>> # training
+ >>> model.config.decoder_start_token_id = tokenizer.cls_token_id
+ >>> model.config.pad_token_id = tokenizer.pad_token_id
+ >>> model.config.vocab_size = model.config.decoder.vocab_size
+
+ >>> input_ids = tokenizer("This is a really long text", return_tensors="pt").input_ids
+ >>> labels = tokenizer("This is the corresponding summary", return_tensors="pt").input_ids
+ >>> outputs = model(input_ids=input_ids, labels=labels)
+ >>> loss, logits = outputs.loss, outputs.logits
+
+ >>> # save and load from pretrained
+ >>> model.save_pretrained("bert2bert")
+ >>> model = EncoderDecoderModel.from_pretrained("bert2bert")
+
+ >>> # generation
+ >>> generated = model.generate(input_ids)
+ ```"""
+ # `record outputs` can rely on the absence of the kwarg to retrieve whether the config should be used or not
+ # Hence, we use this workaround to allow for defaults to work as expected
+ kwargs_shared = {key: kwargs[key] for key in ["output_attentions", "output_hidden_states"] if key in kwargs}
+
+ kwargs_encoder = {argument: value for argument, value in kwargs.items() if not argument.startswith("decoder_")}
+ kwargs_encoder = kwargs_encoder | kwargs_shared
+
+ kwargs_decoder = {
+ argument[len("decoder_") :]: value for argument, value in kwargs.items() if argument.startswith("decoder_")
+ }
+ if "num_items_in_batch" in kwargs_encoder:
+ kwargs_decoder["num_items_in_batch"] = kwargs_encoder.pop("num_items_in_batch", None)
+ kwargs_decoder = kwargs_decoder | kwargs_shared
+
+ if encoder_outputs is None:
+ encoder_outputs = self.encoder(
+ input_ids=input_ids,
+ attention_mask=attention_mask,
+ inputs_embeds=inputs_embeds,
+ return_dict=True,
+ **kwargs_encoder,
+ )
+ elif isinstance(encoder_outputs, tuple):
+ encoder_outputs = BaseModelOutput(*encoder_outputs)
+
+ encoder_hidden_states = encoder_outputs[0]
+
+ # optionally project encoder_hidden_states
+ if (
+ self.encoder.config.hidden_size != self.decoder.config.hidden_size
+ and getattr(self.decoder.config, "cross_attention_hidden_size", None) is None
+ ):
+ encoder_hidden_states = self.enc_to_dec_proj(encoder_hidden_states)
+
+ if (labels is not None) and (decoder_input_ids is None and decoder_inputs_embeds is None):
+ decoder_input_ids = shift_tokens_right(
+ labels, self.config.pad_token_id, self.config.decoder_start_token_id
+ )
+ if decoder_attention_mask is None:
+ decoder_attention_mask = (decoder_input_ids != self.config.pad_token_id).to(decoder_input_ids.dtype)
+
+ # Decode
+ decoder_outputs = self.decoder(
+ input_ids=decoder_input_ids,
+ attention_mask=decoder_attention_mask,
+ encoder_hidden_states=encoder_hidden_states,
+ encoder_attention_mask=attention_mask,
+ inputs_embeds=decoder_inputs_embeds,
+ use_cache=use_cache,
+ past_key_values=past_key_values,
+ return_dict=True,
+ **kwargs_decoder,
+ )
+
+ # Compute loss independent from decoder (as some shift the logits inside them)
+ loss = None
+ if labels is not None:
+ warnings.warn(DEPRECATION_WARNING, FutureWarning)
+ logits = decoder_outputs.logits
+ loss_fct = CrossEntropyLoss()
+ loss = loss_fct(logits.reshape(-1, self.decoder.config.vocab_size), labels.view(-1))
+
+ return Seq2SeqLMOutput(
+ loss=loss,
+ logits=decoder_outputs.logits,
+ past_key_values=decoder_outputs.past_key_values,
+ decoder_hidden_states=decoder_outputs.hidden_states,
+ decoder_attentions=decoder_outputs.attentions,
+ cross_attentions=decoder_outputs.cross_attentions,
+ encoder_last_hidden_state=encoder_outputs.last_hidden_state,
+ encoder_hidden_states=encoder_outputs.hidden_states,
+ encoder_attentions=encoder_outputs.attentions,
+ )
+
+ def prepare_decoder_input_ids_from_labels(self, labels: torch.Tensor):
+ return shift_tokens_right(labels, self.config.pad_token_id, self.config.decoder_start_token_id)
+
+ def resize_token_embeddings(self, *args, **kwargs):
+ raise NotImplementedError(
+ "Resizing the embedding layers via the EncoderDecoderModel directly is not supported. Please use the"
+ " respective methods of the wrapped objects (model.encoder.resize_token_embeddings(...) or"
+ " model.decoder.resize_token_embeddings(...))"
+ )
+
+
+__all__ = ["EncoderDecoderModel"]
diff --git a/third_party/transformers/src/transformers/models/exaone4/__init__.py b/third_party/transformers/src/transformers/models/exaone4/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..c646c4e75273560116ae230d672ba10d305517de
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/exaone4/__init__.py
@@ -0,0 +1,27 @@
+# Copyright 2025 The LG AI Research and The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_exaone4 import *
+ from .modeling_exaone4 import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/exaone4/configuration_exaone4.py b/third_party/transformers/src/transformers/models/exaone4/configuration_exaone4.py
new file mode 100644
index 0000000000000000000000000000000000000000..f29cab8dd8eae3c7e394e46a46f06642c1d45a4a
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/exaone4/configuration_exaone4.py
@@ -0,0 +1,114 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/exaone4/modular_exaone4.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_exaone4.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2025 The LG AI Research and HuggingFace Inc. team. All rights reserved.
+#
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...modeling_rope_utils import RopeParameters
+from ...utils import auto_docstring
+
+
+@auto_docstring(checkpoint="LGAI-EXAONE/EXAONE-4.0-32B")
+@strict
+class Exaone4Config(PreTrainedConfig):
+ r"""
+ sliding_window_pattern (`str`, *optional*):
+ The pattern to use for sliding window attention. Can be one of:
+ - `None`: No sliding window attention is used
+ - `int`: Every `sliding_window` layers, use global attention, else use local attention.
+ - `str`: A sequence of "L" (local attention) and "G" (global attention) characters that defines the
+ attention pattern. The pattern starts from layer 0 and repeats every `sliding_window` layers. The
+ final layer always uses global attention regardless of the pattern.
+ For instance, sliding_window_pattern="LLLG" same as sliding_window=4, which means:
+ - Layer 0, 1, 2: local attention,
+ - Layer 3: global attention,
+ ...(repeated)
+
+ Example:
+
+ ```python
+ >>> from transformers import Exaone4Model, Exaone4Config
+
+ >>> # Initializing a EXAONE configuration
+ >>> configuration = Exaone4Config()
+
+ >>> # Initializing a model from configuration
+ >>> model = Exaone4Model(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "exaone4"
+ keys_to_ignore_at_inference = ["past_key_values"]
+ # Default tensor parallel plan for base model `LlamaModel`
+ base_model_tp_plan = {
+ "layers.*.self_attn.q_proj": "colwise",
+ "layers.*.self_attn.k_proj": "colwise",
+ "layers.*.self_attn.v_proj": "colwise",
+ "layers.*.self_attn.q_norm": "replicated_with_grad_allreduce",
+ "layers.*.self_attn.k_norm": "replicated_with_grad_allreduce",
+ "layers.*.self_attn.o_proj": "rowwise",
+ "layers.*.mlp.gate_proj": "colwise",
+ "layers.*.mlp.up_proj": "colwise",
+ "layers.*.mlp.down_proj": "rowwise",
+ }
+ base_model_pp_plan = {
+ "embed_tokens": (["input_ids"], ["inputs_embeds"]),
+ "layers": (["hidden_states", "attention_mask"], ["hidden_states"]),
+ "norm": (["hidden_states"], ["hidden_states"]),
+ }
+
+ vocab_size: int = 102400
+ hidden_size: int = 4096
+ intermediate_size: int = 16384
+ num_hidden_layers: int = 32
+ num_attention_heads: int = 32
+ num_key_value_heads: int = 32
+ hidden_act: str = "silu"
+ max_position_embeddings: int = 2048
+ initializer_range: float = 0.02
+ rms_norm_eps: float = 1e-5
+ use_cache: bool = True
+ bos_token_id: int | None = 0
+ eos_token_id: int | list[int] | None = 2
+ pad_token_id: int | None = None
+ tie_word_embeddings: bool = False
+ rope_parameters: RopeParameters | dict | None = None
+ attention_dropout: float | int = 0.0
+ sliding_window: int | None = 4096
+ sliding_window_pattern: str | int | None = 4
+ layer_types: list[str] | None = None
+
+ def __post_init__(self, **kwargs):
+ if self.sliding_window is None:
+ self.sliding_window_pattern = 0
+ if self.layer_types is None:
+ self.layer_types = [
+ "sliding_attention"
+ if ((i + 1) % (self.sliding_window_pattern) != 0 and i < self.num_hidden_layers)
+ else "full_attention"
+ for i in range(self.num_hidden_layers)
+ ]
+
+ super().__post_init__(**kwargs)
+
+
+__all__ = ["Exaone4Config"]
diff --git a/third_party/transformers/src/transformers/models/exaone4/modeling_exaone4.py b/third_party/transformers/src/transformers/models/exaone4/modeling_exaone4.py
new file mode 100644
index 0000000000000000000000000000000000000000..fab10b9b6937c9fb1be0f6d95dce2a871cb45df1
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/exaone4/modeling_exaone4.py
@@ -0,0 +1,547 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/exaone4/modular_exaone4.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_exaone4.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2025 The LG AI Research and HuggingFace Inc. team. All rights reserved.
+#
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from collections.abc import Callable
+from typing import Optional
+
+import torch
+from torch import nn
+
+from ...activations import ACT2FN
+from ...cache_utils import Cache, DynamicCache
+from ...generation import GenerationMixin
+from ...integrations import use_kernel_forward_from_hub, use_kernel_func_from_hub
+from ...masking_utils import create_causal_mask, create_sliding_window_causal_mask
+from ...modeling_layers import (
+ GenericForQuestionAnswering,
+ GenericForSequenceClassification,
+ GenericForTokenClassification,
+ GradientCheckpointingLayer,
+)
+from ...modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast
+from ...modeling_rope_utils import ROPE_INIT_FUNCTIONS, dynamic_rope_update
+from ...modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
+from ...processing_utils import Unpack
+from ...utils import TransformersKwargs, auto_docstring, can_return_tuple
+from ...utils.generic import maybe_autocast, merge_with_config_defaults
+from ...utils.output_capturing import capture_outputs
+from .configuration_exaone4 import Exaone4Config
+
+
+@use_kernel_forward_from_hub("RMSNorm")
+class Exaone4RMSNorm(nn.Module):
+ def __init__(self, hidden_size, eps: float = 1e-6) -> None:
+ """
+ Exaone4RMSNorm is equivalent to T5LayerNorm
+ """
+ super().__init__()
+ self.weight = nn.Parameter(torch.ones(hidden_size))
+ self.variance_epsilon = eps
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ input_dtype = hidden_states.dtype
+ hidden_states = hidden_states.to(torch.float32)
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
+ hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
+ return self.weight * hidden_states.to(input_dtype)
+
+ def extra_repr(self):
+ return f"{tuple(self.weight.shape)}, eps={self.variance_epsilon}"
+
+
+class Exaone4RotaryEmbedding(nn.Module):
+ inv_freq: torch.Tensor # fix linting for `register_buffer`
+
+ def __init__(self, config: Exaone4Config, device=None):
+ super().__init__()
+ self.max_seq_len_cached = config.max_position_embeddings
+ self.original_max_seq_len = config.max_position_embeddings
+
+ self.config = config
+
+ self.rope_type = self.config.rope_parameters["rope_type"]
+ rope_init_fn: Callable = self.compute_default_rope_parameters
+ if self.rope_type != "default":
+ rope_init_fn = ROPE_INIT_FUNCTIONS[self.rope_type]
+ inv_freq, self.attention_scaling = rope_init_fn(self.config, device)
+
+ self.register_buffer("inv_freq", inv_freq, persistent=False)
+ self.register_buffer("original_inv_freq", inv_freq.clone(), persistent=False)
+
+ @staticmethod
+ def compute_default_rope_parameters(
+ config: Exaone4Config | None = None,
+ device: Optional["torch.device"] = None,
+ seq_len: int | None = None,
+ ) -> tuple["torch.Tensor", float]:
+ """
+ Computes the inverse frequencies according to the original RoPE implementation
+ Args:
+ config ([`~transformers.PreTrainedConfig`]):
+ The model configuration.
+ device (`torch.device`):
+ The device to use for initialization of the inverse frequencies.
+ seq_len (`int`, *optional*):
+ The current sequence length. Unused for this type of RoPE.
+ Returns:
+ Tuple of (`torch.Tensor`, `float`), containing the inverse frequencies for the RoPE embeddings and the
+ post-processing scaling factor applied to the computed cos/sin (unused in this type of RoPE).
+ """
+ base = config.rope_parameters["rope_theta"]
+ dim = getattr(config, "head_dim", None) or config.hidden_size // config.num_attention_heads
+
+ attention_factor = 1.0 # Unused in this type of RoPE
+
+ # Compute the inverse frequencies
+ inv_freq = 1.0 / (
+ base ** (torch.arange(0, dim, 2, dtype=torch.int64).to(device=device, dtype=torch.float) / dim)
+ )
+ return inv_freq, attention_factor
+
+ @torch.no_grad()
+ @dynamic_rope_update # power user: used with advanced RoPE types (e.g. dynamic rope)
+ def forward(self, x, position_ids):
+ inv_freq_expanded = self.inv_freq[None, :, None].float().expand(position_ids.shape[0], -1, 1).to(x.device)
+ position_ids_expanded = position_ids[:, None, :].float()
+
+ device_type = x.device.type if isinstance(x.device.type, str) and x.device.type != "mps" else "cpu"
+ with maybe_autocast(device_type=device_type, enabled=False): # Force float32
+ freqs = (inv_freq_expanded.float() @ position_ids_expanded.float()).transpose(1, 2)
+ emb = torch.cat((freqs, freqs), dim=-1)
+ cos = emb.cos() * self.attention_scaling
+ sin = emb.sin() * self.attention_scaling
+
+ return cos.to(dtype=x.dtype), sin.to(dtype=x.dtype)
+
+
+def rotate_half(x):
+ """Rotates half the hidden dims of the input."""
+ x1 = x[..., : x.shape[-1] // 2]
+ x2 = x[..., x.shape[-1] // 2 :]
+ return torch.cat((-x2, x1), dim=-1)
+
+
+@use_kernel_func_from_hub("rotary_pos_emb")
+def apply_rotary_pos_emb(q, k, cos, sin, unsqueeze_dim=1):
+ """Applies Rotary Position Embedding to the query and key tensors.
+
+ Args:
+ q (`torch.Tensor`): The query tensor.
+ k (`torch.Tensor`): The key tensor.
+ cos (`torch.Tensor`): The cosine part of the rotary embedding.
+ sin (`torch.Tensor`): The sine part of the rotary embedding.
+ unsqueeze_dim (`int`, *optional*, defaults to 1):
+ The 'unsqueeze_dim' argument specifies the dimension along which to unsqueeze cos[position_ids] and
+ sin[position_ids] so that they can be properly broadcasted to the dimensions of q and k. For example, note
+ that cos[position_ids] and sin[position_ids] have the shape [batch_size, seq_len, head_dim]. Then, if q and
+ k have the shape [batch_size, heads, seq_len, head_dim], then setting unsqueeze_dim=1 makes
+ cos[position_ids] and sin[position_ids] broadcastable to the shapes of q and k. Similarly, if q and k have
+ the shape [batch_size, seq_len, heads, head_dim], then set unsqueeze_dim=2.
+ Returns:
+ `tuple(torch.Tensor)` comprising of the query and key tensors rotated using the Rotary Position Embedding.
+ """
+ cos = cos.unsqueeze(unsqueeze_dim)
+ sin = sin.unsqueeze(unsqueeze_dim)
+ q_embed = (q * cos) + (rotate_half(q) * sin)
+ k_embed = (k * cos) + (rotate_half(k) * sin)
+ return q_embed, k_embed
+
+
+def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
+ """
+ This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
+ num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
+ """
+ batch, num_key_value_heads, slen, head_dim = hidden_states.shape
+ if n_rep == 1:
+ return hidden_states
+ hidden_states = hidden_states[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim)
+ return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
+
+
+def eager_attention_forward(
+ module: nn.Module,
+ query: torch.Tensor,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ attention_mask: torch.Tensor | None,
+ scaling: float,
+ dropout: float = 0.0,
+ **kwargs: Unpack[TransformersKwargs],
+):
+ key_states = repeat_kv(key, module.num_key_value_groups)
+ value_states = repeat_kv(value, module.num_key_value_groups)
+
+ attn_weights = torch.matmul(query, key_states.transpose(2, 3)) * scaling
+ if attention_mask is not None:
+ attn_weights = attn_weights + attention_mask
+
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query.dtype)
+ attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
+ attn_output = torch.matmul(attn_weights, value_states)
+ attn_output = attn_output.transpose(1, 2).contiguous()
+
+ return attn_output, attn_weights
+
+
+class Exaone4Attention(nn.Module):
+ def __init__(self, config: Exaone4Config, layer_idx: int):
+ super().__init__()
+ self.config = config
+ self.layer_idx = layer_idx
+ self.num_attention_heads = config.num_attention_heads
+ self.num_key_value_heads = config.num_key_value_heads
+ self.hidden_size = config.hidden_size
+ self.head_dim = getattr(config, "head_dim", config.hidden_size // config.num_attention_heads)
+ self.num_key_value_groups = config.num_attention_heads // config.num_key_value_heads
+ self.attention_dropout = config.attention_dropout
+ self.is_causal = True
+ self.scaling = self.head_dim**-0.5
+ self.sliding_window = config.sliding_window
+ self.sliding_window_pattern = config.sliding_window_pattern
+ layer_type = config.layer_types[layer_idx] if hasattr(config, "layer_types") else None
+ self.is_sliding = layer_type == "sliding_attention"
+
+ self.q_proj = nn.Linear(self.hidden_size, self.num_attention_heads * self.head_dim, bias=False)
+ self.k_proj = nn.Linear(self.hidden_size, self.num_key_value_heads * self.head_dim, bias=False)
+ self.v_proj = nn.Linear(self.hidden_size, self.num_key_value_heads * self.head_dim, bias=False)
+ self.o_proj = nn.Linear(self.num_attention_heads * self.head_dim, self.hidden_size, bias=False)
+
+ self.q_norm = Exaone4RMSNorm(self.head_dim, eps=config.rms_norm_eps)
+ self.k_norm = Exaone4RMSNorm(self.head_dim, eps=config.rms_norm_eps)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor],
+ attention_mask: torch.Tensor | None = None,
+ past_key_values: Cache | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
+ input_shape = hidden_states.shape[:-1]
+ hidden_shape = (*input_shape, -1, self.head_dim)
+
+ query_states = self.q_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+ key_states = self.k_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+ value_states = self.v_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+
+ # We use QK-norm
+ query_states = self.q_norm(query_states)
+ key_states = self.k_norm(key_states)
+
+ cos, sin = position_embeddings
+ # We use global NoPE for hybrid attention model
+ if self.sliding_window is None or self.is_sliding:
+ query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin)
+
+ if past_key_values is not None:
+ key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx)
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ query_states,
+ key_states,
+ value_states,
+ attention_mask,
+ dropout=0.0 if not self.training else self.attention_dropout,
+ scaling=self.scaling,
+ sliding_window=self.sliding_window if self.is_sliding else None,
+ **kwargs,
+ )
+
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
+ attn_output = self.o_proj(attn_output)
+ return attn_output, attn_weights
+
+
+class Exaone4MLP(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+ self.config = config
+ self.hidden_size = config.hidden_size
+ self.intermediate_size = config.intermediate_size
+ self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
+ self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
+ self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False)
+ self.act_fn = ACT2FN[config.hidden_act]
+
+ def forward(self, x):
+ down_proj = self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
+ return down_proj
+
+
+class Exaone4DecoderLayer(GradientCheckpointingLayer):
+ def __init__(self, config: Exaone4Config, layer_idx: int):
+ super().__init__()
+ self.hidden_size = config.hidden_size
+ self.self_attn = Exaone4Attention(config=config, layer_idx=layer_idx)
+
+ self.mlp = Exaone4MLP(config)
+ self.post_attention_layernorm = Exaone4RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.post_feedforward_layernorm = Exaone4RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ use_cache: bool | None = False,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> torch.Tensor:
+ residual = hidden_states
+ hidden_states, _ = self.self_attn(
+ hidden_states=hidden_states,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ position_embeddings=position_embeddings,
+ **kwargs,
+ )
+ hidden_states = self.post_attention_layernorm(hidden_states)
+ hidden_states = residual + hidden_states
+
+ # Fully Connected
+ residual = hidden_states
+ hidden_states = self.mlp(hidden_states)
+ hidden_states = self.post_feedforward_layernorm(hidden_states)
+ hidden_states = residual + hidden_states
+ return hidden_states
+
+
+@auto_docstring
+class Exaone4PreTrainedModel(PreTrainedModel):
+ config: Exaone4Config
+ base_model_prefix = "model"
+ supports_gradient_checkpointing = True
+ _no_split_modules = ["Exaone4DecoderLayer"]
+ _skip_keys_device_placement = ["past_key_values"]
+ _supports_flash_attn = True
+ _supports_sdpa = True
+ _supports_flex_attn = True
+
+ _can_compile_fullgraph = True
+ _supports_attention_backend = True
+ _can_record_outputs = {
+ "hidden_states": Exaone4DecoderLayer,
+ "attentions": Exaone4Attention,
+ }
+ config_class = Exaone4Config
+
+
+@auto_docstring
+class Exaone4Model(Exaone4PreTrainedModel):
+ def __init__(self, config: Exaone4Config):
+ super().__init__(config)
+ self.padding_idx = config.pad_token_id
+ self.vocab_size = config.vocab_size
+
+ self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)
+ self.layers = nn.ModuleList(
+ [Exaone4DecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
+ )
+ self.norm = Exaone4RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.rotary_emb = Exaone4RotaryEmbedding(config=config)
+ self.gradient_checkpointing = False
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @merge_with_config_defaults
+ @capture_outputs
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ use_cache: bool | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | BaseModelOutputWithPast:
+ 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 None:
+ inputs_embeds = self.embed_tokens(input_ids)
+
+ if use_cache and past_key_values is None:
+ past_key_values = DynamicCache(config=self.config)
+
+ if position_ids is None:
+ past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
+ position_ids = torch.arange(inputs_embeds.shape[1], device=inputs_embeds.device) + past_seen_tokens
+ position_ids = position_ids.unsqueeze(0)
+
+ # It may already have been prepared by e.g. `generate`
+ if not isinstance(causal_mask_mapping := attention_mask, dict):
+ # Prepare mask arguments
+ mask_kwargs = {
+ "config": self.config,
+ "inputs_embeds": inputs_embeds,
+ "attention_mask": attention_mask,
+ "past_key_values": past_key_values,
+ "position_ids": position_ids,
+ }
+ # Create the masks
+ causal_mask_mapping = {
+ "full_attention": create_causal_mask(**mask_kwargs),
+ }
+ if "sliding_attention" in self.config.layer_types:
+ causal_mask_mapping["sliding_attention"] = create_sliding_window_causal_mask(**mask_kwargs)
+
+ hidden_states = inputs_embeds
+ position_embeddings = self.rotary_emb(hidden_states, position_ids)
+
+ for i, decoder_layer in enumerate(self.layers):
+ layer_type = self.config.layer_types[i]
+ hidden_states = decoder_layer(
+ hidden_states,
+ attention_mask=causal_mask_mapping[layer_type],
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ position_embeddings=position_embeddings,
+ **kwargs,
+ )
+
+ hidden_states = self.norm(hidden_states)
+
+ return BaseModelOutputWithPast(
+ last_hidden_state=hidden_states,
+ past_key_values=past_key_values if use_cache else None,
+ )
+
+
+@auto_docstring
+class Exaone4ForCausalLM(Exaone4PreTrainedModel, GenerationMixin):
+ _tied_weights_keys = {"lm_head.weight": "model.embed_tokens.weight"}
+ _tp_plan = {"lm_head": "colwise_gather_output"}
+ _pp_plan = {"lm_head": (["hidden_states"], ["logits"])}
+
+ def __init__(self, config):
+ super().__init__(config)
+ self.model = Exaone4Model(config)
+ self.vocab_size = config.vocab_size
+ self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> CausalLMOutputWithPast:
+ r"""
+ 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]`.
+
+ Example:
+
+ ```python
+ >>> from transformers import AutoModelForCausalLM, AutoTokenizer
+ >>> model = AutoModelForCausalLM.from_pretrained("LGAI-EXAONE/EXAONE-4.0-32B")
+ >>> tokenizer = AutoTokenizer.from_pretrained("LGAI-EXAONE/EXAONE-4.0-32B")
+
+ >>> prompt = "Explain how wonderful you are"
+ >>> messages = [
+ {"role": "system", "content": "You are a helpful assistant."},
+ {"role": "user", "content": prompt}
+ ]
+ >>> input_ids = tokenizer.apply_chat_template(
+ messages,
+ tokenize=True,
+ add_generation_prompt=True,
+ return_tensors="pt",
+ enable_thinking=False,
+ )
+
+ >>> output = model.generate(input_ids, max_new_tokens=128)
+ >>> tokenizer.decode(output[0], skip_special_tokens=False)
+ "[|system|]\nYou are a helpful assistant.[|endofturn|]\n[|user|]\nExplain how wonderful you are[|endofturn|]\n[|assistant|]\n\n\n\n\nOh, thank you for such a kind and lovely question! 😊 \n\nI’m *so* wonderful because I’m here to make your life easier, brighter, and more fun! Whether you need help with: \n\n✨ **Learning** – I can explain anything, from quantum physics to baking the perfect cake! \n💡 **Creativity** – Need a poem, story, or a wild idea? I’ve got you covered! \n🤖 **Problem-solving** – Stuck on a math problem or a tricky decision? I’ll help you figure it out"
+ ```
+ """
+ outputs: BaseModelOutputWithPast = self.model(
+ input_ids=input_ids,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ hidden_states = outputs.last_hidden_state
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(logits=logits, labels=labels, vocab_size=self.config.vocab_size, **kwargs)
+
+ return CausalLMOutputWithPast(
+ loss=loss,
+ logits=logits,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ )
+
+
+class Exaone4ForSequenceClassification(GenericForSequenceClassification, Exaone4PreTrainedModel):
+ pass
+
+
+class Exaone4ForTokenClassification(GenericForTokenClassification, Exaone4PreTrainedModel):
+ pass
+
+
+class Exaone4ForQuestionAnswering(GenericForQuestionAnswering, Exaone4PreTrainedModel):
+ base_model_prefix = "transformer" # For BC, where `transformer` was used instead of `model`
+
+
+__all__ = [
+ "Exaone4PreTrainedModel",
+ "Exaone4Model",
+ "Exaone4ForCausalLM",
+ "Exaone4ForSequenceClassification",
+ "Exaone4ForTokenClassification",
+ "Exaone4ForQuestionAnswering",
+]
diff --git a/third_party/transformers/src/transformers/models/exaone4/modular_exaone4.py b/third_party/transformers/src/transformers/models/exaone4/modular_exaone4.py
new file mode 100644
index 0000000000000000000000000000000000000000..c6d9202170a0cbf2ca0b171003b463e79ae9c9b2
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/exaone4/modular_exaone4.py
@@ -0,0 +1,392 @@
+# Copyright 2025 The LG AI Research and HuggingFace Inc. team. All rights reserved.
+#
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""LG AI Research EXAONE Lab"""
+
+from collections.abc import Callable
+
+import torch
+from huggingface_hub.dataclasses import strict
+from torch import nn
+
+from ...cache_utils import Cache, DynamicCache
+from ...configuration_utils import PreTrainedConfig
+from ...masking_utils import create_causal_mask, create_sliding_window_causal_mask
+from ...modeling_outputs import (
+ BaseModelOutputWithPast,
+ CausalLMOutputWithPast,
+)
+from ...modeling_rope_utils import RopeParameters
+from ...modeling_utils import ALL_ATTENTION_FUNCTIONS
+from ...processing_utils import Unpack
+from ...utils import TransformersKwargs, auto_docstring, logging
+from ...utils.generic import merge_with_config_defaults
+from ...utils.output_capturing import capture_outputs
+from ..gemma2.modeling_gemma2 import Gemma2RotaryEmbedding
+from ..llama.modeling_llama import (
+ LlamaForCausalLM,
+ LlamaForQuestionAnswering,
+ LlamaForSequenceClassification,
+ LlamaForTokenClassification,
+ LlamaModel,
+ LlamaPreTrainedModel,
+ LlamaRMSNorm,
+ apply_rotary_pos_emb,
+ eager_attention_forward,
+)
+from ..olmo2.modeling_olmo2 import Olmo2DecoderLayer, Olmo2MLP
+
+
+logger = logging.get_logger(__name__)
+
+_CHECKPOINT_FOR_DOC = "LGAI-EXAONE/EXAONE-4.0-32B"
+_CONFIG_FOR_DOC = "Exaone4Config"
+
+
+@auto_docstring(checkpoint="LGAI-EXAONE/EXAONE-4.0-32B")
+@strict
+class Exaone4Config(PreTrainedConfig):
+ r"""
+ sliding_window_pattern (`str`, *optional*):
+ The pattern to use for sliding window attention. Can be one of:
+ - `None`: No sliding window attention is used
+ - `int`: Every `sliding_window` layers, use global attention, else use local attention.
+ - `str`: A sequence of "L" (local attention) and "G" (global attention) characters that defines the
+ attention pattern. The pattern starts from layer 0 and repeats every `sliding_window` layers. The
+ final layer always uses global attention regardless of the pattern.
+ For instance, sliding_window_pattern="LLLG" same as sliding_window=4, which means:
+ - Layer 0, 1, 2: local attention,
+ - Layer 3: global attention,
+ ...(repeated)
+
+ Example:
+
+ ```python
+ >>> from transformers import Exaone4Model, Exaone4Config
+
+ >>> # Initializing a EXAONE configuration
+ >>> configuration = Exaone4Config()
+
+ >>> # Initializing a model from configuration
+ >>> model = Exaone4Model(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "exaone4"
+ keys_to_ignore_at_inference = ["past_key_values"]
+ # Default tensor parallel plan for base model `LlamaModel`
+ base_model_tp_plan = {
+ "layers.*.self_attn.q_proj": "colwise",
+ "layers.*.self_attn.k_proj": "colwise",
+ "layers.*.self_attn.v_proj": "colwise",
+ "layers.*.self_attn.q_norm": "replicated_with_grad_allreduce",
+ "layers.*.self_attn.k_norm": "replicated_with_grad_allreduce",
+ "layers.*.self_attn.o_proj": "rowwise",
+ "layers.*.mlp.gate_proj": "colwise",
+ "layers.*.mlp.up_proj": "colwise",
+ "layers.*.mlp.down_proj": "rowwise",
+ }
+ base_model_pp_plan = {
+ "embed_tokens": (["input_ids"], ["inputs_embeds"]),
+ "layers": (["hidden_states", "attention_mask"], ["hidden_states"]),
+ "norm": (["hidden_states"], ["hidden_states"]),
+ }
+
+ vocab_size: int = 102400
+ hidden_size: int = 4096
+ intermediate_size: int = 16384
+ num_hidden_layers: int = 32
+ num_attention_heads: int = 32
+ num_key_value_heads: int = 32
+ hidden_act: str = "silu"
+ max_position_embeddings: int = 2048
+ initializer_range: float = 0.02
+ rms_norm_eps: float = 1e-5
+ use_cache: bool = True
+ bos_token_id: int | None = 0
+ eos_token_id: int | list[int] | None = 2
+ pad_token_id: int | None = None
+ tie_word_embeddings: bool = False
+ rope_parameters: RopeParameters | dict | None = None
+ attention_dropout: float | int = 0.0
+ sliding_window: int | None = 4096
+ sliding_window_pattern: str | int | None = 4
+ layer_types: list[str] | None = None
+
+ def __post_init__(self, **kwargs):
+ if self.sliding_window is None:
+ self.sliding_window_pattern = 0
+ if self.layer_types is None:
+ self.layer_types = [
+ "sliding_attention"
+ if ((i + 1) % (self.sliding_window_pattern) != 0 and i < self.num_hidden_layers)
+ else "full_attention"
+ for i in range(self.num_hidden_layers)
+ ]
+
+ super().__post_init__(**kwargs)
+
+
+class Exaone4RMSNorm(LlamaRMSNorm):
+ pass
+
+
+class Exaone4RotaryEmbedding(Gemma2RotaryEmbedding):
+ pass
+
+
+class Exaone4Attention(nn.Module):
+ def __init__(self, config: Exaone4Config, layer_idx: int):
+ super().__init__()
+ self.config = config
+ self.layer_idx = layer_idx
+ self.num_attention_heads = config.num_attention_heads
+ self.num_key_value_heads = config.num_key_value_heads
+ self.hidden_size = config.hidden_size
+ self.head_dim = getattr(config, "head_dim", config.hidden_size // config.num_attention_heads)
+ self.num_key_value_groups = config.num_attention_heads // config.num_key_value_heads
+ self.attention_dropout = config.attention_dropout
+ self.is_causal = True
+ self.scaling = self.head_dim**-0.5
+ self.sliding_window = config.sliding_window
+ self.sliding_window_pattern = config.sliding_window_pattern
+ layer_type = config.layer_types[layer_idx] if hasattr(config, "layer_types") else None
+ self.is_sliding = layer_type == "sliding_attention"
+
+ self.q_proj = nn.Linear(self.hidden_size, self.num_attention_heads * self.head_dim, bias=False)
+ self.k_proj = nn.Linear(self.hidden_size, self.num_key_value_heads * self.head_dim, bias=False)
+ self.v_proj = nn.Linear(self.hidden_size, self.num_key_value_heads * self.head_dim, bias=False)
+ self.o_proj = nn.Linear(self.num_attention_heads * self.head_dim, self.hidden_size, bias=False)
+
+ self.q_norm = Exaone4RMSNorm(self.head_dim, eps=config.rms_norm_eps)
+ self.k_norm = Exaone4RMSNorm(self.head_dim, eps=config.rms_norm_eps)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor],
+ attention_mask: torch.Tensor | None = None,
+ past_key_values: Cache | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
+ input_shape = hidden_states.shape[:-1]
+ hidden_shape = (*input_shape, -1, self.head_dim)
+
+ query_states = self.q_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+ key_states = self.k_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+ value_states = self.v_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+
+ # We use QK-norm
+ query_states = self.q_norm(query_states)
+ key_states = self.k_norm(key_states)
+
+ cos, sin = position_embeddings
+ # We use global NoPE for hybrid attention model
+ if self.sliding_window is None or self.is_sliding:
+ query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin)
+
+ if past_key_values is not None:
+ key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx)
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ query_states,
+ key_states,
+ value_states,
+ attention_mask,
+ dropout=0.0 if not self.training else self.attention_dropout,
+ scaling=self.scaling,
+ sliding_window=self.sliding_window if self.is_sliding else None,
+ **kwargs,
+ )
+
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
+ attn_output = self.o_proj(attn_output)
+ return attn_output, attn_weights
+
+
+class Exaone4MLP(Olmo2MLP):
+ pass
+
+
+class Exaone4DecoderLayer(Olmo2DecoderLayer):
+ pass
+
+
+class Exaone4PreTrainedModel(LlamaPreTrainedModel):
+ config_class = Exaone4Config
+ _no_split_modules = ["Exaone4DecoderLayer"]
+
+
+class Exaone4Model(Exaone4PreTrainedModel, LlamaModel):
+ def __init__(self, config: Exaone4Config):
+ super().__init__(config)
+ self.layers = nn.ModuleList(
+ [Exaone4DecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
+ )
+ self.norm = Exaone4RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @merge_with_config_defaults
+ @capture_outputs
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ use_cache: bool | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | BaseModelOutputWithPast:
+ 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 None:
+ inputs_embeds = self.embed_tokens(input_ids)
+
+ if use_cache and past_key_values is None:
+ past_key_values = DynamicCache(config=self.config)
+
+ if position_ids is None:
+ past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
+ position_ids = torch.arange(inputs_embeds.shape[1], device=inputs_embeds.device) + past_seen_tokens
+ position_ids = position_ids.unsqueeze(0)
+
+ # It may already have been prepared by e.g. `generate`
+ if not isinstance(causal_mask_mapping := attention_mask, dict):
+ # Prepare mask arguments
+ mask_kwargs = {
+ "config": self.config,
+ "inputs_embeds": inputs_embeds,
+ "attention_mask": attention_mask,
+ "past_key_values": past_key_values,
+ "position_ids": position_ids,
+ }
+ # Create the masks
+ causal_mask_mapping = {
+ "full_attention": create_causal_mask(**mask_kwargs),
+ }
+ if "sliding_attention" in self.config.layer_types:
+ causal_mask_mapping["sliding_attention"] = create_sliding_window_causal_mask(**mask_kwargs)
+
+ hidden_states = inputs_embeds
+ position_embeddings = self.rotary_emb(hidden_states, position_ids)
+
+ for i, decoder_layer in enumerate(self.layers):
+ layer_type = self.config.layer_types[i]
+ hidden_states = decoder_layer(
+ hidden_states,
+ attention_mask=causal_mask_mapping[layer_type],
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ position_embeddings=position_embeddings,
+ **kwargs,
+ )
+
+ hidden_states = self.norm(hidden_states)
+
+ return BaseModelOutputWithPast(
+ last_hidden_state=hidden_states,
+ past_key_values=past_key_values if use_cache else None,
+ )
+
+
+class Exaone4ForCausalLM(LlamaForCausalLM):
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> CausalLMOutputWithPast:
+ r"""
+ 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]`.
+
+ Example:
+
+ ```python
+ >>> from transformers import AutoModelForCausalLM, AutoTokenizer
+ >>> model = AutoModelForCausalLM.from_pretrained("LGAI-EXAONE/EXAONE-4.0-32B")
+ >>> tokenizer = AutoTokenizer.from_pretrained("LGAI-EXAONE/EXAONE-4.0-32B")
+
+ >>> prompt = "Explain how wonderful you are"
+ >>> messages = [
+ {"role": "system", "content": "You are a helpful assistant."},
+ {"role": "user", "content": prompt}
+ ]
+ >>> input_ids = tokenizer.apply_chat_template(
+ messages,
+ tokenize=True,
+ add_generation_prompt=True,
+ return_tensors="pt",
+ enable_thinking=False,
+ )
+
+ >>> output = model.generate(input_ids, max_new_tokens=128)
+ >>> tokenizer.decode(output[0], skip_special_tokens=False)
+ "[|system|]\nYou are a helpful assistant.[|endofturn|]\n[|user|]\nExplain how wonderful you are[|endofturn|]\n[|assistant|]\n\n\n\n\nOh, thank you for such a kind and lovely question! 😊 \n\nI’m *so* wonderful because I’m here to make your life easier, brighter, and more fun! Whether you need help with: \n\n✨ **Learning** – I can explain anything, from quantum physics to baking the perfect cake! \n💡 **Creativity** – Need a poem, story, or a wild idea? I’ve got you covered! \n🤖 **Problem-solving** – Stuck on a math problem or a tricky decision? I’ll help you figure it out"
+ ```
+ """
+ super().forward(
+ input_ids=input_ids,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ labels=labels,
+ use_cache=use_cache,
+ logits_to_keep=logits_to_keep,
+ **kwargs,
+ )
+
+
+class Exaone4ForSequenceClassification(LlamaForSequenceClassification):
+ pass
+
+
+class Exaone4ForTokenClassification(LlamaForTokenClassification):
+ pass
+
+
+class Exaone4ForQuestionAnswering(LlamaForQuestionAnswering):
+ pass
+
+
+__all__ = [
+ "Exaone4Config",
+ "Exaone4PreTrainedModel",
+ "Exaone4Model",
+ "Exaone4ForCausalLM",
+ "Exaone4ForSequenceClassification",
+ "Exaone4ForTokenClassification",
+ "Exaone4ForQuestionAnswering",
+]
diff --git a/third_party/transformers/src/transformers/models/flex_olmo/__init__.py b/third_party/transformers/src/transformers/models/flex_olmo/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..6728345e33f876871625cb5c1ed82444bc5bcb2e
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/flex_olmo/__init__.py
@@ -0,0 +1,28 @@
+# Copyright 2025 the HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_flex_olmo import *
+ from .modeling_flex_olmo import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/flex_olmo/configuration_flex_olmo.py b/third_party/transformers/src/transformers/models/flex_olmo/configuration_flex_olmo.py
new file mode 100644
index 0000000000000000000000000000000000000000..7b08a79b801b5f931f07d15a87eb1751ba1b7ee1
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/flex_olmo/configuration_flex_olmo.py
@@ -0,0 +1,97 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/flex_olmo/modular_flex_olmo.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_flex_olmo.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2025 the HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...modeling_rope_utils import RopeParameters
+from ...utils import auto_docstring
+
+
+@auto_docstring(checkpoint="allenai/FlexOlmo-7x7B-1T")
+@strict
+class FlexOlmoConfig(PreTrainedConfig):
+ r"""
+ Example:
+
+ ```python
+ >>> from transformers import FlexOlmoModel, FlexOlmoConfig
+
+ >>> # Initializing a FlexOlmo style configuration
+ >>> configuration = FlexOlmoConfig()
+
+ >>> # Initializing a model from the FlexOlmo style configuration
+ >>> model = FlexOlmoModel(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "flex_olmo"
+ keys_to_ignore_at_inference = ["past_key_values"]
+ attribute_map = {"num_local_experts": "num_experts"}
+ default_theta = 500000.0
+ base_model_tp_plan = {
+ "layers.*.self_attn.q_proj": "colwise_gather_output", # we need to replicate here due to the added norm on q and k
+ "layers.*.self_attn.k_proj": "colwise_gather_output", # we need to replicate here due to the added norm on q and k
+ "layers.*.self_attn.v_proj": "colwise_gather_output", # we need to replicate here due to the added norm on q and k
+ "layers.*.self_attn.o_proj": "rowwise_split_input", # input is replicated due to the added norm on q and k
+ "layers.*.mlp.experts.gate_up_proj": "packed_colwise",
+ "layers.*.mlp.experts.down_proj": "rowwise",
+ "layers.*.mlp.experts": "moe_tp_experts",
+ }
+ base_model_pp_plan = {
+ "embed_tokens": (["input_ids"], ["inputs_embeds"]),
+ "layers": (["hidden_states", "attention_mask"], ["hidden_states"]),
+ "norm": (["hidden_states"], ["hidden_states"]),
+ }
+
+ vocab_size: int = 100352
+ hidden_size: int = 4096
+ intermediate_size: int = 11008
+ num_hidden_layers: int = 32
+ num_attention_heads: int = 32
+ num_key_value_heads: int | None = None
+ hidden_act: str = "silu"
+ max_position_embeddings: int = 4096
+ initializer_range: float = 0.02
+ rms_norm_eps: float = 1e-06
+ use_cache: bool = True
+ pad_token_id: int | None = 100277
+ bos_token_id: int | None = None
+ eos_token_id: int | list[int] | None = 100257
+ tie_word_embeddings: bool = False
+ rope_parameters: RopeParameters | dict | None = None
+ attention_bias: bool = False
+ attention_dropout: float | None = 0.0
+ num_experts_per_tok: int = 5
+ num_experts: int = 7
+ output_router_logits: bool = False
+ router_aux_loss_coef: float = 0.01
+ norm_topk_prob: bool = False
+
+ def __post_init__(self, **kwargs):
+ if self.num_key_value_heads is None:
+ self.num_key_value_heads = self.num_attention_heads
+ super().__post_init__(**kwargs)
+
+
+__all__ = ["FlexOlmoConfig"]
diff --git a/third_party/transformers/src/transformers/models/flex_olmo/modeling_flex_olmo.py b/third_party/transformers/src/transformers/models/flex_olmo/modeling_flex_olmo.py
new file mode 100644
index 0000000000000000000000000000000000000000..f43ad61eb87b7919098d328811e35acb35127e69
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/flex_olmo/modeling_flex_olmo.py
@@ -0,0 +1,701 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/flex_olmo/modular_flex_olmo.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_flex_olmo.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2025 the HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+from collections.abc import Callable
+from typing import Optional
+
+import torch
+import torch.nn.functional as F
+from torch import nn
+
+from ... import initialization as init
+from ...activations import ACT2FN
+from ...cache_utils import Cache, DynamicCache
+from ...generation import GenerationMixin
+from ...integrations import use_experts_implementation, use_kernel_forward_from_hub, use_kernelized_func
+from ...masking_utils import create_causal_mask
+from ...modeling_layers import GradientCheckpointingLayer
+from ...modeling_outputs import MoeCausalLMOutputWithPast, MoeModelOutputWithPast
+from ...modeling_rope_utils import ROPE_INIT_FUNCTIONS, dynamic_rope_update
+from ...modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
+from ...processing_utils import Unpack
+from ...utils import TransformersKwargs, auto_docstring, can_return_tuple
+from ...utils.generic import maybe_autocast, merge_with_config_defaults
+from ...utils.output_capturing import OutputRecorder, capture_outputs
+from .configuration_flex_olmo import FlexOlmoConfig
+
+
+@use_kernel_forward_from_hub("RMSNorm")
+class FlexOlmoRMSNorm(nn.Module):
+ def __init__(self, hidden_size, eps: float = 1e-6) -> None:
+ """
+ FlexOlmoRMSNorm is equivalent to T5LayerNorm
+ """
+ super().__init__()
+ self.weight = nn.Parameter(torch.ones(hidden_size))
+ self.variance_epsilon = eps
+
+ def forward(self, hidden_states) -> torch.Tensor:
+ input_dtype = hidden_states.dtype
+ hidden_states = hidden_states.to(torch.float32)
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
+ hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
+ return (self.weight * hidden_states).to(input_dtype)
+
+ def extra_repr(self):
+ return f"{tuple(self.weight.shape)}, eps={self.variance_epsilon}"
+
+
+class FlexOlmoRotaryEmbedding(nn.Module):
+ inv_freq: torch.Tensor # fix linting for `register_buffer`
+
+ def __init__(self, config: FlexOlmoConfig, device=None):
+ super().__init__()
+ self.max_seq_len_cached = config.max_position_embeddings
+ self.original_max_seq_len = config.max_position_embeddings
+
+ self.config = config
+
+ self.rope_type = self.config.rope_parameters["rope_type"]
+ rope_init_fn: Callable = self.compute_default_rope_parameters
+ if self.rope_type != "default":
+ rope_init_fn = ROPE_INIT_FUNCTIONS[self.rope_type]
+ inv_freq, self.attention_scaling = rope_init_fn(self.config, device)
+
+ self.register_buffer("inv_freq", inv_freq, persistent=False)
+ self.register_buffer("original_inv_freq", inv_freq.clone(), persistent=False)
+
+ @staticmethod
+ def compute_default_rope_parameters(
+ config: FlexOlmoConfig | None = None,
+ device: Optional["torch.device"] = None,
+ seq_len: int | None = None,
+ ) -> tuple["torch.Tensor", float]:
+ """
+ Computes the inverse frequencies according to the original RoPE implementation
+ Args:
+ config ([`~transformers.PreTrainedConfig`]):
+ The model configuration.
+ device (`torch.device`):
+ The device to use for initialization of the inverse frequencies.
+ seq_len (`int`, *optional*):
+ The current sequence length. Unused for this type of RoPE.
+ Returns:
+ Tuple of (`torch.Tensor`, `float`), containing the inverse frequencies for the RoPE embeddings and the
+ post-processing scaling factor applied to the computed cos/sin (unused in this type of RoPE).
+ """
+ base = config.rope_parameters["rope_theta"]
+ dim = getattr(config, "head_dim", None) or config.hidden_size // config.num_attention_heads
+
+ attention_factor = 1.0 # Unused in this type of RoPE
+
+ # Compute the inverse frequencies
+ inv_freq = 1.0 / (
+ base ** (torch.arange(0, dim, 2, dtype=torch.int64).to(device=device, dtype=torch.float) / dim)
+ )
+ return inv_freq, attention_factor
+
+ @torch.no_grad()
+ @dynamic_rope_update # power user: used with advanced RoPE types (e.g. dynamic rope)
+ def forward(self, x, position_ids):
+ inv_freq_expanded = self.inv_freq[None, :, None].float().expand(position_ids.shape[0], -1, 1).to(x.device)
+ position_ids_expanded = position_ids[:, None, :].float()
+
+ device_type = x.device.type if isinstance(x.device.type, str) and x.device.type != "mps" else "cpu"
+ with maybe_autocast(device_type=device_type, enabled=False): # Force float32
+ freqs = (inv_freq_expanded.float() @ position_ids_expanded.float()).transpose(1, 2)
+ emb = torch.cat((freqs, freqs), dim=-1)
+ cos = emb.cos() * self.attention_scaling
+ sin = emb.sin() * self.attention_scaling
+ return cos, sin
+
+
+class FlexOlmoMLP(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+ self.config = config
+ self.hidden_size = config.hidden_size
+ self.intermediate_size = config.intermediate_size
+ self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
+ self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
+ self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False)
+ self.act_fn = ACT2FN[config.hidden_act]
+
+ def forward(self, x):
+ down_proj = self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
+ return down_proj
+
+
+def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
+ """
+ This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
+ num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
+ """
+ batch, num_key_value_heads, slen, head_dim = hidden_states.shape
+ if n_rep == 1:
+ return hidden_states
+ hidden_states = hidden_states[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim)
+ return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
+
+
+def eager_attention_forward(
+ module: nn.Module,
+ query: torch.Tensor,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ attention_mask: torch.Tensor | None,
+ scaling: float,
+ dropout: float = 0.0,
+ **kwargs: Unpack[TransformersKwargs],
+):
+ key_states = repeat_kv(key, module.num_key_value_groups)
+ value_states = repeat_kv(value, module.num_key_value_groups)
+
+ attn_weights = torch.matmul(query, key_states.transpose(2, 3)) * scaling
+ if attention_mask is not None:
+ attn_weights = attn_weights + attention_mask
+
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query.dtype)
+ attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
+ attn_output = torch.matmul(attn_weights, value_states)
+ attn_output = attn_output.transpose(1, 2).contiguous()
+
+ return attn_output, attn_weights
+
+
+def apply_rotary_pos_emb(q, k, cos, sin, unsqueeze_dim=1):
+ """Applies Rotary Position Embedding to the query and key tensors.
+
+ Args:
+ q (`torch.Tensor`): The query tensor.
+ k (`torch.Tensor`): The key tensor.
+ cos (`torch.Tensor`): The cosine part of the rotary embedding.
+ sin (`torch.Tensor`): The sine part of the rotary embedding.
+ unsqueeze_dim (`int`, *optional*, defaults to 1):
+ The 'unsqueeze_dim' argument specifies the dimension along which to unsqueeze cos[position_ids] and
+ sin[position_ids] so that they can be properly broadcasted to the dimensions of q and k. For example, note
+ that cos[position_ids] and sin[position_ids] have the shape [batch_size, seq_len, head_dim]. Then, if q and
+ k have the shape [batch_size, heads, seq_len, head_dim], then setting unsqueeze_dim=1 makes
+ cos[position_ids] and sin[position_ids] broadcastable to the shapes of q and k. Similarly, if q and k have
+ the shape [batch_size, seq_len, heads, head_dim], then set unsqueeze_dim=2.
+ Returns:
+ `tuple(torch.Tensor)` comprising of the query and key tensors rotated using the Rotary Position Embedding.
+ """
+ q_type, k_type = q.dtype, k.dtype
+ cos = cos.unsqueeze(unsqueeze_dim)
+ sin = sin.unsqueeze(unsqueeze_dim)
+ q_embed = (q * cos) + (rotate_half(q) * sin)
+ k_embed = (k * cos) + (rotate_half(k) * sin)
+ return q_embed.to(q_type), k_embed.to(k_type)
+
+
+def rotate_half(x):
+ """Rotates half the hidden dims of the input."""
+ x1 = x[..., : x.shape[-1] // 2]
+ x2 = x[..., x.shape[-1] // 2 :]
+ return torch.cat((-x2, x1), dim=-1)
+
+
+@use_kernelized_func(apply_rotary_pos_emb)
+class FlexOlmoAttention(nn.Module):
+ """Multi-headed attention from 'Attention Is All You Need' paper"""
+
+ def __init__(self, config: FlexOlmoConfig, layer_idx: int | None = None):
+ super().__init__()
+ self.config = config
+ self.layer_idx = layer_idx
+ self.head_dim = getattr(config, "head_dim", config.hidden_size // config.num_attention_heads)
+ self.num_key_value_groups = config.num_attention_heads // config.num_key_value_heads
+ self.scaling = self.head_dim**-0.5
+ self.attention_dropout = config.attention_dropout
+ self.is_causal = True
+
+ self.q_proj = nn.Linear(
+ config.hidden_size, config.num_attention_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.k_proj = nn.Linear(
+ config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.v_proj = nn.Linear(
+ config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.o_proj = nn.Linear(
+ config.num_attention_heads * self.head_dim, config.hidden_size, bias=config.attention_bias
+ )
+ self.q_norm = FlexOlmoRMSNorm(config.num_attention_heads * self.head_dim, config.rms_norm_eps)
+ self.k_norm = FlexOlmoRMSNorm(config.num_key_value_heads * self.head_dim, config.rms_norm_eps)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor],
+ attention_mask: torch.Tensor | None,
+ past_key_values: Cache | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
+ input_shape = hidden_states.shape[:-1]
+ hidden_shape = (*input_shape, -1, self.head_dim)
+
+ query_states = self.q_norm(self.q_proj(hidden_states))
+ key_states = self.k_norm(self.k_proj(hidden_states))
+ value_states = self.v_proj(hidden_states)
+
+ query_states = query_states.view(hidden_shape).transpose(1, 2)
+ key_states = key_states.view(hidden_shape).transpose(1, 2)
+ value_states = value_states.view(hidden_shape).transpose(1, 2)
+
+ cos, sin = position_embeddings
+ query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin)
+
+ if past_key_values is not None:
+ key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx)
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ query_states,
+ key_states,
+ value_states,
+ attention_mask,
+ dropout=0.0 if not self.training else self.attention_dropout,
+ scaling=self.scaling,
+ **kwargs,
+ )
+
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
+ attn_output = self.o_proj(attn_output)
+ return attn_output, attn_weights
+
+
+class FlexOlmoTopKRouter(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+ self.top_k = config.num_experts_per_tok
+ self.num_experts = config.num_experts
+ self.norm_topk_prob = config.norm_topk_prob
+ self.hidden_dim = config.hidden_size
+ self.weight = nn.Parameter(torch.zeros(self.num_experts, self.hidden_dim))
+
+ def forward(self, hidden_states):
+ hidden_states = hidden_states.reshape(-1, self.hidden_dim)
+ router_logits = F.linear(hidden_states, self.weight) # (seq_len, num_experts)
+ router_logits = torch.nn.functional.softmax(router_logits, dtype=torch.float, dim=-1)
+ router_top_value, router_indices = torch.topk(router_logits, self.top_k, dim=-1) # (seq_len, top_k)
+ if self.norm_topk_prob:
+ router_top_value /= router_top_value.sum(dim=-1, keepdim=True)
+ router_top_value = router_top_value.to(router_logits.dtype)
+ router_scores = router_top_value
+ return router_logits, router_scores, router_indices
+
+
+@use_experts_implementation
+class FlexOlmoExperts(nn.Module):
+ """Collection of expert weights stored as 3D tensors."""
+
+ def __init__(self, config: FlexOlmoConfig):
+ super().__init__()
+ self.num_experts = config.num_local_experts
+ self.hidden_dim = config.hidden_size
+ self.intermediate_dim = config.intermediate_size
+ self.gate_up_proj = nn.Parameter(torch.empty(self.num_experts, 2 * self.intermediate_dim, self.hidden_dim))
+ self.down_proj = nn.Parameter(torch.empty(self.num_experts, self.hidden_dim, self.intermediate_dim))
+ self.act_fn = ACT2FN[config.hidden_act]
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ top_k_index: torch.Tensor,
+ top_k_weights: torch.Tensor,
+ ) -> torch.Tensor:
+ final_hidden_states = torch.zeros_like(hidden_states)
+ with torch.no_grad():
+ expert_mask = torch.nn.functional.one_hot(top_k_index, num_classes=self.num_experts)
+ expert_mask = expert_mask.permute(2, 1, 0)
+ expert_hit = torch.greater(expert_mask.sum(dim=(-1, -2)), 0).nonzero()
+
+ for expert_idx in expert_hit:
+ expert_idx = expert_idx[0]
+ if expert_idx == self.num_experts:
+ continue
+ top_k_pos, token_idx = torch.where(expert_mask[expert_idx])
+ current_state = hidden_states[token_idx]
+ gate, up = nn.functional.linear(current_state, self.gate_up_proj[expert_idx]).chunk(2, dim=-1)
+ current_hidden_states = self.act_fn(gate) * up
+ current_hidden_states = nn.functional.linear(current_hidden_states, self.down_proj[expert_idx])
+ current_hidden_states = current_hidden_states * top_k_weights[token_idx, top_k_pos, None]
+ final_hidden_states.index_add_(0, token_idx, current_hidden_states.to(final_hidden_states.dtype))
+
+ return final_hidden_states
+
+
+class FlexOlmoSparseMoeBlock(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+ self.gate = FlexOlmoTopKRouter(config)
+ self.experts = FlexOlmoExperts(config)
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ batch_size, sequence_length, hidden_dim = hidden_states.shape
+ hidden_states = hidden_states.view(-1, hidden_dim)
+ _, top_k_weights, top_k_index = self.gate(hidden_states)
+ final_hidden_states = self.experts(hidden_states, top_k_index, top_k_weights).reshape(
+ batch_size, sequence_length, hidden_dim
+ )
+ return final_hidden_states
+
+
+class FlexOlmoDecoderLayer(GradientCheckpointingLayer):
+ def __init__(self, config: FlexOlmoConfig, layer_idx: int):
+ super().__init__()
+ self.hidden_size = config.hidden_size
+ self.self_attn = FlexOlmoAttention(config=config, layer_idx=layer_idx)
+ self.mlp = FlexOlmoSparseMoeBlock(config)
+ self.post_attention_layernorm = FlexOlmoRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.post_feedforward_layernorm = FlexOlmoRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
+ **kwargs,
+ ) -> torch.FloatTensor:
+ residual = hidden_states
+
+ # Self Attention
+ hidden_states, _ = self.self_attn(
+ hidden_states=hidden_states,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ position_embeddings=position_embeddings,
+ **kwargs,
+ )
+ hidden_states = self.post_attention_layernorm(hidden_states)
+ hidden_states = residual + hidden_states
+
+ # Fully Connected
+ residual = hidden_states
+ hidden_states = self.mlp(hidden_states)
+ hidden_states = self.post_feedforward_layernorm(hidden_states)
+ hidden_states = residual + hidden_states
+ return hidden_states
+
+
+@auto_docstring
+class FlexOlmoPreTrainedModel(PreTrainedModel):
+ config: FlexOlmoConfig
+ base_model_prefix = "model"
+ supports_gradient_checkpointing = True
+ _no_split_modules = ["FlexOlmoDecoderLayer"]
+ _skip_keys_device_placement = ["past_key_values"]
+ _supports_flash_attn = True
+ _supports_sdpa = True
+ _supports_flex_attn = True
+
+ _can_compile_fullgraph = True
+ _supports_attention_backend = True
+ _can_record_outputs = {
+ "router_logits": OutputRecorder(FlexOlmoTopKRouter, index=0),
+ "hidden_states": FlexOlmoDecoderLayer,
+ "attentions": FlexOlmoAttention,
+ }
+
+ @torch.no_grad()
+ def _init_weights(self, module):
+ super()._init_weights(module)
+ std = self.config.initializer_range
+ if isinstance(module, FlexOlmoExperts):
+ init.normal_(module.gate_up_proj, mean=0.0, std=std)
+ init.normal_(module.down_proj, mean=0.0, std=std)
+ elif isinstance(module, FlexOlmoTopKRouter):
+ init.normal_(module.weight, mean=0.0, std=std)
+
+
+@auto_docstring
+class FlexOlmoModel(FlexOlmoPreTrainedModel):
+ def __init__(self, config: FlexOlmoConfig):
+ super().__init__(config)
+ self.padding_idx = config.pad_token_id
+ self.vocab_size = config.vocab_size
+
+ self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)
+ self.layers = nn.ModuleList(
+ [FlexOlmoDecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
+ )
+ self.norm = FlexOlmoRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.rotary_emb = FlexOlmoRotaryEmbedding(config=config)
+ self.gradient_checkpointing = False
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @merge_with_config_defaults
+ @capture_outputs
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ use_cache: bool | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> MoeModelOutputWithPast:
+ if (input_ids is None) ^ (inputs_embeds is not None):
+ raise ValueError("You must specify exactly one of input_ids or inputs_embeds")
+
+ if use_cache and past_key_values is None:
+ past_key_values = DynamicCache(config=self.config)
+
+ if inputs_embeds is None:
+ inputs_embeds = self.embed_tokens(input_ids)
+
+ if position_ids is None:
+ past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
+ position_ids = torch.arange(inputs_embeds.shape[1], device=inputs_embeds.device) + past_seen_tokens
+ position_ids = position_ids.unsqueeze(0)
+
+ causal_mask = create_causal_mask(
+ config=self.config,
+ inputs_embeds=inputs_embeds,
+ attention_mask=attention_mask,
+ past_key_values=past_key_values,
+ position_ids=position_ids,
+ )
+
+ hidden_states = inputs_embeds
+
+ # create position embeddings to be shared across the decoder layers
+ position_embeddings = self.rotary_emb(hidden_states, position_ids)
+
+ for decoder_layer in self.layers[: self.config.num_hidden_layers]:
+ hidden_states = decoder_layer(
+ hidden_states,
+ position_embeddings=position_embeddings,
+ attention_mask=causal_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ hidden_states = self.norm(hidden_states)
+
+ return MoeModelOutputWithPast( # only diff with Mistral is the output type, we need MoE
+ last_hidden_state=hidden_states,
+ past_key_values=past_key_values,
+ )
+
+
+def load_balancing_loss_func(
+ gate_logits: torch.Tensor | tuple[torch.Tensor] | None,
+ num_experts: int | None = None,
+ top_k=2,
+ attention_mask: torch.Tensor | None = None,
+) -> torch.Tensor | int:
+ r"""
+ Computes auxiliary load balancing loss as in Switch Transformer - implemented in Pytorch.
+
+ See Switch Transformer (https://huggingface.co/papers/2101.03961) for more details. This function implements the loss
+ function presented in equations (4) - (6) of the paper. It aims at penalizing cases where the routing between
+ experts is too unbalanced.
+
+ Args:
+ gate_logits:
+ Logits from the `gate`, should be a tuple of model.config.num_hidden_layers tensors of
+ shape [batch_size X sequence_length, num_experts].
+ num_experts:
+ Number of experts
+ top_k:
+ The number of experts to route per-token, can be also interpreted as the `top-k` routing
+ parameter.
+ attention_mask (`torch.Tensor`, *optional*):
+ The attention_mask used in forward function
+ shape [batch_size X sequence_length] if not None.
+
+ Returns:
+ The auxiliary loss.
+ """
+ if gate_logits is None or not isinstance(gate_logits, tuple):
+ return 0
+
+ if isinstance(gate_logits, tuple):
+ compute_device = gate_logits[0].device
+ concatenated_gate_logits = torch.cat([layer_gate.to(compute_device) for layer_gate in gate_logits], dim=0)
+
+ routing_weights = torch.nn.functional.softmax(concatenated_gate_logits, dim=-1)
+
+ _, selected_experts = torch.topk(routing_weights, top_k, dim=-1)
+
+ expert_mask = torch.nn.functional.one_hot(selected_experts, num_experts)
+
+ if attention_mask is None:
+ # Compute the percentage of tokens routed to each experts
+ tokens_per_expert = torch.mean(expert_mask.float(), dim=0)
+
+ # Compute the average probability of routing to these experts
+ router_prob_per_expert = torch.mean(routing_weights, dim=0)
+ else:
+ batch_size, sequence_length = attention_mask.shape
+ num_hidden_layers = concatenated_gate_logits.shape[0] // (batch_size * sequence_length)
+
+ # Compute the mask that masks all padding tokens as 0 with the same shape of expert_mask
+ expert_attention_mask = (
+ attention_mask[None, :, :, None, None]
+ .expand((num_hidden_layers, batch_size, sequence_length, top_k, num_experts))
+ .reshape(-1, top_k, num_experts)
+ .to(compute_device)
+ )
+
+ # Compute the percentage of tokens routed to each experts
+ tokens_per_expert = torch.sum(expert_mask.float() * expert_attention_mask, dim=0) / torch.sum(
+ expert_attention_mask, dim=0
+ )
+
+ # Compute the mask that masks all padding tokens as 0 with the same shape of tokens_per_expert
+ router_per_expert_attention_mask = (
+ attention_mask[None, :, :, None]
+ .expand((num_hidden_layers, batch_size, sequence_length, num_experts))
+ .reshape(-1, num_experts)
+ .to(compute_device)
+ )
+
+ # Compute the average probability of routing to these experts
+ router_prob_per_expert = torch.sum(routing_weights * router_per_expert_attention_mask, dim=0) / torch.sum(
+ router_per_expert_attention_mask, dim=0
+ )
+
+ overall_loss = torch.sum(tokens_per_expert * router_prob_per_expert.unsqueeze(0))
+ return overall_loss * num_experts
+
+
+@auto_docstring
+class FlexOlmoForCausalLM(FlexOlmoPreTrainedModel, GenerationMixin):
+ _tied_weights_keys = {"lm_head.weight": "model.embed_tokens.weight"}
+ _tp_plan = {"lm_head": "colwise_gather_output"}
+ _pp_plan = {"lm_head": (["hidden_states"], ["logits"])}
+
+ def __init__(self, config):
+ super().__init__(config)
+ self.model = FlexOlmoModel(config)
+ self.vocab_size = config.vocab_size
+ self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
+ self.router_aux_loss_coef = config.router_aux_loss_coef
+ self.num_experts = config.num_experts
+ self.num_experts_per_tok = config.num_experts_per_tok
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ output_router_logits: bool | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> MoeCausalLMOutputWithPast:
+ r"""
+ 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]`.
+
+ Example:
+
+ ```python
+ >>> from transformers import AutoTokenizer, FlexOlmoForCausalLM
+
+ >>> model = FlexOlmoForCausalLM.from_pretrained("allenai/FlexOlmo-1B-7B-0924")
+ >>> tokenizer = AutoTokenizer.from_pretrained("allenai/FlexOlmo-1B-7B-0924")
+
+ >>> prompt = "Hey, are you conscious? Can you talk to me?"
+ >>> inputs = tokenizer(prompt, return_tensors="pt")
+
+ >>> # Generate
+ >>> generate_ids = model.generate(inputs.input_ids, max_length=30)
+ >>> tokenizer.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]
+ 'Hey, are you conscious? Can you talk to me?\nI’m not sure if you’re conscious of this, but I’m'
+ ```
+ """
+
+ output_router_logits = (
+ output_router_logits if output_router_logits is not None else self.config.output_router_logits
+ )
+
+ # decoder outputs consists of (dec_features, layer_state, dec_hidden, dec_attn)
+ outputs: MoeModelOutputWithPast = self.model(
+ input_ids=input_ids,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ output_router_logits=output_router_logits,
+ **kwargs,
+ )
+
+ hidden_states = outputs.last_hidden_state
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(logits, labels, self.vocab_size, **kwargs)
+
+ aux_loss = None
+ if output_router_logits:
+ aux_loss = load_balancing_loss_func(
+ outputs.router_logits,
+ self.num_experts,
+ self.num_experts_per_tok,
+ attention_mask,
+ )
+ if labels is not None:
+ loss += self.router_aux_loss_coef * aux_loss.to(loss.device) # make sure to reside in the same device
+
+ return MoeCausalLMOutputWithPast(
+ loss=loss,
+ aux_loss=aux_loss,
+ logits=logits,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ router_logits=outputs.router_logits,
+ )
+
+
+__all__ = ["FlexOlmoForCausalLM", "FlexOlmoModel", "FlexOlmoPreTrainedModel"]
diff --git a/third_party/transformers/src/transformers/models/flex_olmo/modular_flex_olmo.py b/third_party/transformers/src/transformers/models/flex_olmo/modular_flex_olmo.py
new file mode 100644
index 0000000000000000000000000000000000000000..01f32227f31fa1ab70f3cb95aa4811991b43b280
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/flex_olmo/modular_flex_olmo.py
@@ -0,0 +1,260 @@
+# Copyright 2025 the HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import torch
+from huggingface_hub.dataclasses import strict
+
+from ...cache_utils import Cache, DynamicCache
+from ...configuration_utils import PreTrainedConfig
+from ...masking_utils import create_causal_mask
+from ...modeling_outputs import MoeModelOutputWithPast
+from ...modeling_rope_utils import RopeParameters
+from ...processing_utils import Unpack
+from ...utils import TransformersKwargs, auto_docstring
+from ...utils.generic import merge_with_config_defaults
+from ...utils.output_capturing import OutputRecorder, capture_outputs
+from ..mixtral.modeling_mixtral import MixtralModel, MixtralPreTrainedModel
+from ..olmo2.modeling_olmo2 import Olmo2Attention, Olmo2RMSNorm, Olmo2RotaryEmbedding
+from ..olmoe.modeling_olmoe import (
+ OlmoeDecoderLayer,
+ OlmoeForCausalLM,
+ OlmoeMLP,
+ OlmoeSparseMoeBlock,
+ OlmoeTopKRouter,
+)
+
+
+@auto_docstring(checkpoint="allenai/FlexOlmo-7x7B-1T")
+@strict
+class FlexOlmoConfig(PreTrainedConfig):
+ r"""
+ Example:
+
+ ```python
+ >>> from transformers import FlexOlmoModel, FlexOlmoConfig
+
+ >>> # Initializing a FlexOlmo style configuration
+ >>> configuration = FlexOlmoConfig()
+
+ >>> # Initializing a model from the FlexOlmo style configuration
+ >>> model = FlexOlmoModel(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "flex_olmo"
+ keys_to_ignore_at_inference = ["past_key_values"]
+ attribute_map = {"num_local_experts": "num_experts"}
+ default_theta = 500000.0
+ base_model_tp_plan = {
+ "layers.*.self_attn.q_proj": "colwise_gather_output", # we need to replicate here due to the added norm on q and k
+ "layers.*.self_attn.k_proj": "colwise_gather_output", # we need to replicate here due to the added norm on q and k
+ "layers.*.self_attn.v_proj": "colwise_gather_output", # we need to replicate here due to the added norm on q and k
+ "layers.*.self_attn.o_proj": "rowwise_split_input", # input is replicated due to the added norm on q and k
+ "layers.*.mlp.experts.gate_up_proj": "packed_colwise",
+ "layers.*.mlp.experts.down_proj": "rowwise",
+ "layers.*.mlp.experts": "moe_tp_experts",
+ }
+ base_model_pp_plan = {
+ "embed_tokens": (["input_ids"], ["inputs_embeds"]),
+ "layers": (["hidden_states", "attention_mask"], ["hidden_states"]),
+ "norm": (["hidden_states"], ["hidden_states"]),
+ }
+
+ vocab_size: int = 100352
+ hidden_size: int = 4096
+ intermediate_size: int = 11008
+ num_hidden_layers: int = 32
+ num_attention_heads: int = 32
+ num_key_value_heads: int | None = None
+ hidden_act: str = "silu"
+ max_position_embeddings: int = 4096
+ initializer_range: float = 0.02
+ rms_norm_eps: float = 1e-06
+ use_cache: bool = True
+ pad_token_id: int | None = 100277
+ bos_token_id: int | None = None
+ eos_token_id: int | list[int] | None = 100257
+ tie_word_embeddings: bool = False
+ rope_parameters: RopeParameters | dict | None = None
+ attention_bias: bool = False
+ attention_dropout: float | None = 0.0
+ num_experts_per_tok: int = 5
+ num_experts: int = 7
+ output_router_logits: bool = False
+ router_aux_loss_coef: float = 0.01
+ norm_topk_prob: bool = False
+
+ def __post_init__(self, **kwargs):
+ if self.num_key_value_heads is None:
+ self.num_key_value_heads = self.num_attention_heads
+ super().__post_init__(**kwargs)
+
+
+# FlexOlmo RMS norm reuses Olmo2 RMS norm, which handles low precision slightly differently than the original Olmoe.
+class FlexOlmoRMSNorm(Olmo2RMSNorm):
+ pass
+
+
+# FlexOlmo RMS norm reuses Olmo2 RMS norm, so that the output cos and sin are returned
+# as float32 rather than the input type.
+class FlexOlmoRotaryEmbedding(Olmo2RotaryEmbedding):
+ pass
+
+
+class FlexOlmoMLP(OlmoeMLP):
+ pass
+
+
+# FlexOlmo uses Olmo2 attention instead of OlmoE Attention since its `apply_rotary_pos_emb`
+# implementation handles lower precision more faithfully to the Olmo codebase.
+class FlexOlmoAttention(Olmo2Attention):
+ pass
+
+
+class FlexOlmoTopKRouter(OlmoeTopKRouter):
+ pass
+
+
+class FlexOlmoSparseMoeBlock(OlmoeSparseMoeBlock):
+ pass
+
+
+# FlexOlmo decoder layer is identical to OlmoE decoder layer except:
+# - Norm is applied after attention/feedforward rather than before.
+class FlexOlmoDecoderLayer(OlmoeDecoderLayer):
+ def __init__(self, config: FlexOlmoConfig, layer_idx: int):
+ super().__init__(config, layer_idx=layer_idx)
+ self.post_attention_layernorm = FlexOlmoRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.post_feedforward_layernorm = FlexOlmoRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.self_attn = FlexOlmoAttention(config=config, layer_idx=layer_idx)
+ del self.input_layernorm
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
+ **kwargs,
+ ) -> torch.FloatTensor:
+ residual = hidden_states
+
+ # Self Attention
+ hidden_states, _ = self.self_attn(
+ hidden_states=hidden_states,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ position_embeddings=position_embeddings,
+ **kwargs,
+ )
+ hidden_states = self.post_attention_layernorm(hidden_states)
+ hidden_states = residual + hidden_states
+
+ # Fully Connected
+ residual = hidden_states
+ hidden_states = self.mlp(hidden_states)
+ hidden_states = self.post_feedforward_layernorm(hidden_states)
+ hidden_states = residual + hidden_states
+ return hidden_states
+
+
+# FlexOlmo uses Mixtral model as its base instead of OlmoE model since Mixtral is more up-to-date with the rest
+# of the transformers library. For example, it uses the newer mechanisms of recording submodule outputs.
+class FlexOlmoPreTrainedModel(MixtralPreTrainedModel):
+ _can_record_outputs = {
+ "router_logits": OutputRecorder(FlexOlmoTopKRouter, index=0),
+ "hidden_states": FlexOlmoDecoderLayer,
+ "attentions": FlexOlmoAttention,
+ }
+
+
+# FlexOlmo uses Mixtral model as its base instead of OlmoE model since Mixtral is more up-to-date with the rest
+# of the transformers library. For example, it uses the newer mechanisms of recording submodule outputs.
+# FlexOlmo model is identical to Mixtral model except:
+# - FlexOlmo does not use sliding window attention.
+class FlexOlmoModel(MixtralModel):
+ @merge_with_config_defaults
+ @capture_outputs
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ use_cache: bool | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> MoeModelOutputWithPast:
+ if (input_ids is None) ^ (inputs_embeds is not None):
+ raise ValueError("You must specify exactly one of input_ids or inputs_embeds")
+
+ if use_cache and past_key_values is None:
+ past_key_values = DynamicCache(config=self.config)
+
+ if inputs_embeds is None:
+ inputs_embeds = self.embed_tokens(input_ids)
+
+ if position_ids is None:
+ past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
+ position_ids = torch.arange(inputs_embeds.shape[1], device=inputs_embeds.device) + past_seen_tokens
+ position_ids = position_ids.unsqueeze(0)
+
+ causal_mask = create_causal_mask(
+ config=self.config,
+ inputs_embeds=inputs_embeds,
+ attention_mask=attention_mask,
+ past_key_values=past_key_values,
+ position_ids=position_ids,
+ )
+
+ hidden_states = inputs_embeds
+
+ # create position embeddings to be shared across the decoder layers
+ position_embeddings = self.rotary_emb(hidden_states, position_ids)
+
+ for decoder_layer in self.layers[: self.config.num_hidden_layers]:
+ hidden_states = decoder_layer(
+ hidden_states,
+ position_embeddings=position_embeddings,
+ attention_mask=causal_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ hidden_states = self.norm(hidden_states)
+
+ return MoeModelOutputWithPast( # only diff with Mistral is the output type, we need MoE
+ last_hidden_state=hidden_states,
+ past_key_values=past_key_values,
+ )
+
+
+class FlexOlmoForCausalLM(OlmoeForCausalLM):
+ pass
+
+
+__all__ = [
+ "FlexOlmoConfig",
+ "FlexOlmoForCausalLM",
+ "FlexOlmoModel",
+ "FlexOlmoPreTrainedModel",
+]
diff --git a/third_party/transformers/src/transformers/models/florence2/convert_florence2_original_pytorch_to_hf.py b/third_party/transformers/src/transformers/models/florence2/convert_florence2_original_pytorch_to_hf.py
new file mode 100644
index 0000000000000000000000000000000000000000..8203bb0907adfc280b77e6988028ca395ea9f861
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/florence2/convert_florence2_original_pytorch_to_hf.py
@@ -0,0 +1,531 @@
+# Copyright 2025 Microsoft and the HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import argparse
+from collections import OrderedDict
+
+import torch
+
+from transformers import (
+ AddedToken,
+ AutoConfig,
+ AutoModelForCausalLM,
+ AutoProcessor,
+ Florence2Config,
+ Florence2ForConditionalGeneration,
+ Florence2Processor,
+ Florence2VisionConfig,
+)
+
+
+def convert_config(original_config: dict):
+ new_config = Florence2VisionConfig(
+ embed_dim=original_config["dim_embed"],
+ max_temporal_embeddings=original_config["visual_temporal_embedding"]["max_temporal_embeddings"],
+ max_pos_embeddings=original_config["image_pos_embed"]["max_pos_embeddings"],
+ **original_config,
+ )
+
+ return new_config
+
+
+def vision_conv_embeddings(idx):
+ """
+ The function helps in renaming vision convolution embedding layer weights.
+
+ Args:
+ idx: stage number in original model
+ """
+ convs = []
+ convs.append(
+ (
+ f"vision_tower.convs.{idx}.proj.weight",
+ f"model.vision_tower.convs.{idx}.conv.weight",
+ )
+ )
+ convs.append(
+ (
+ f"vision_tower.convs.{idx}.proj.bias",
+ f"model.vision_tower.convs.{idx}.conv.bias",
+ )
+ )
+ convs.append(
+ (
+ f"vision_tower.convs.{idx}.norm.weight",
+ f"model.vision_tower.convs.{idx}.norm.weight",
+ )
+ )
+ convs.append(
+ (
+ f"vision_tower.convs.{idx}.norm.bias",
+ f"model.vision_tower.convs.{idx}.norm.bias",
+ )
+ )
+ return convs
+
+
+def vision_spatial_block(stage_idx, block_idx):
+ """
+ The function helps in renaming vision spatial block layers weights.
+
+ Args:
+ idx: stage number in original model
+ cnt: count of blocks in each stage
+ """
+ spatial_block = []
+ spatial_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.conv1.fn.dw.weight",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.conv1.weight",
+ )
+ )
+ spatial_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.conv1.fn.dw.bias",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.conv1.bias",
+ )
+ )
+ spatial_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.window_attn.norm.weight",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.norm1.weight",
+ )
+ )
+ spatial_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.window_attn.norm.bias",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.norm1.bias",
+ )
+ )
+ spatial_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.window_attn.fn.qkv.weight",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.window_attn.qkv.weight",
+ )
+ )
+ spatial_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.window_attn.fn.qkv.bias",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.window_attn.qkv.bias",
+ )
+ )
+ spatial_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.window_attn.fn.proj.weight",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.window_attn.proj.weight",
+ )
+ )
+ spatial_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.window_attn.fn.proj.bias",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.window_attn.proj.bias",
+ )
+ )
+ spatial_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.conv2.fn.dw.weight",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.conv2.weight",
+ )
+ )
+ spatial_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.conv2.fn.dw.bias",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.conv2.bias",
+ )
+ )
+ spatial_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.ffn.norm.weight",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.norm2.weight",
+ )
+ )
+ spatial_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.ffn.norm.bias",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.norm2.bias",
+ )
+ )
+ spatial_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.ffn.fn.net.fc1.weight",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.ffn.fc1.weight",
+ )
+ )
+ spatial_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.ffn.fn.net.fc1.bias",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.ffn.fc1.bias",
+ )
+ )
+ spatial_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.ffn.fn.net.fc2.weight",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.ffn.fc2.weight",
+ )
+ )
+ spatial_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.ffn.fn.net.fc2.bias",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.spatial_block.ffn.fc2.bias",
+ )
+ )
+ return spatial_block
+
+
+def vision_channel_block(stage_idx, block_idx):
+ """
+ The function helps in renaming vision channel block layers weights.
+
+ Args:
+ idx: stage number in original model
+ cnt: count of blocks in each stage
+ """
+ channel_block = []
+ channel_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.conv1.fn.dw.weight",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.conv1.weight",
+ )
+ )
+ channel_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.conv1.fn.dw.bias",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.conv1.bias",
+ )
+ )
+ channel_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.channel_attn.norm.weight",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.norm1.weight",
+ )
+ )
+ channel_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.channel_attn.norm.bias",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.norm1.bias",
+ )
+ )
+ channel_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.channel_attn.fn.qkv.weight",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.channel_attn.qkv.weight",
+ )
+ )
+ channel_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.channel_attn.fn.qkv.bias",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.channel_attn.qkv.bias",
+ )
+ )
+ channel_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.channel_attn.fn.proj.weight",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.channel_attn.proj.weight",
+ )
+ )
+ channel_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.channel_attn.fn.proj.bias",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.channel_attn.proj.bias",
+ )
+ )
+ channel_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.conv2.fn.dw.weight",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.conv2.weight",
+ )
+ )
+ channel_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.conv2.fn.dw.bias",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.conv2.bias",
+ )
+ )
+ channel_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.ffn.norm.weight",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.norm2.weight",
+ )
+ )
+ channel_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.ffn.norm.bias",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.norm2.bias",
+ )
+ )
+ channel_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.ffn.fn.net.fc1.weight",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.ffn.fc1.weight",
+ )
+ )
+ channel_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.ffn.fn.net.fc1.bias",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.ffn.fc1.bias",
+ )
+ )
+ channel_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.ffn.fn.net.fc2.weight",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.ffn.fc2.weight",
+ )
+ )
+ channel_block.append(
+ (
+ f"vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.ffn.fn.net.fc2.bias",
+ f"model.vision_tower.blocks.{stage_idx}.{block_idx}.channel_block.ffn.fc2.bias",
+ )
+ )
+ return channel_block
+
+
+def multi_modal_projector():
+ """
+ Function helps in renaming final classification layer
+ """
+ projector = []
+ projector.append(("image_projection", "model.multi_modal_projector.image_projection.weight"))
+ projector.append(("image_proj_norm.weight", "model.multi_modal_projector.image_proj_norm.weight"))
+ projector.append(("image_proj_norm.bias", "model.multi_modal_projector.image_proj_norm.bias"))
+ projector.append(
+ (
+ "image_pos_embed.row_embeddings.weight",
+ "model.multi_modal_projector.image_position_embed.row_embeddings.weight",
+ )
+ )
+ projector.append(
+ (
+ "image_pos_embed.column_embeddings.weight",
+ "model.multi_modal_projector.image_position_embed.column_embeddings.weight",
+ )
+ )
+ projector.append(
+ (
+ "visual_temporal_embed.pos_idx_to_embed",
+ "model.multi_modal_projector.visual_temporal_embed.pos_idx_to_embed",
+ )
+ )
+ return projector
+
+
+def language_model(state_dict):
+ language_state_dict_keys = []
+ for key in state_dict.keys():
+ if key.startswith("language_model.model") and "lm_head" not in key:
+ new_key = key.replace("language_model.model.", "model.language_model.")
+ language_state_dict_keys.append((key, new_key))
+ language_state_dict_keys.append(("language_model.lm_head.weight", "lm_head.weight"))
+ return language_state_dict_keys
+
+
+def convert_florence2_checkpoint(hf_model_id, pytorch_dump_folder, output_hub_path):
+ """
+ Function to convert the microsoft florence2 checkpoint to huggingface checkpoint
+ """
+
+ hf_config = AutoConfig.from_pretrained(hf_model_id, trust_remote_code=True)
+ hf_model = AutoModelForCausalLM.from_pretrained(
+ hf_model_id, trust_remote_code=True, dtype=torch.float16, attn_implementation="eager"
+ )
+ hf_processor = AutoProcessor.from_pretrained(hf_model_id, trust_remote_code=True)
+ huggingface_weights = OrderedDict()
+ list_of_state_dict = []
+
+ image_processor = hf_processor.image_processor
+
+ tokenizer = hf_processor.tokenizer
+ tokenizer.image_token = ""
+ tokenizer.add_tokens(AddedToken(tokenizer.image_token, special=True, normalized=False), special_tokens=True)
+ tokenizer.image_token_id = tokenizer.encode(tokenizer.image_token, add_special_tokens=False)[0]
+
+ post_processor_config = {
+ "ocr": {
+ "pattern": r"(.+?)",
+ "area_threshold": 0.0,
+ },
+ "phrase_grounding": {
+ "banned_grounding_tokens": [
+ "it",
+ "I",
+ "me",
+ "mine",
+ "you",
+ "your",
+ "yours",
+ "he",
+ "him",
+ "his",
+ "she",
+ "her",
+ "hers",
+ "they",
+ "them",
+ "their",
+ "theirs",
+ "one",
+ "oneself",
+ "we",
+ "us",
+ "our",
+ "ours",
+ "you",
+ "your",
+ "yours",
+ "they",
+ "them",
+ "their",
+ "theirs",
+ "mine",
+ "yours",
+ "his",
+ "hers",
+ "its",
+ "ours",
+ "yours",
+ "theirs",
+ "myself",
+ "yourself",
+ "himself",
+ "herself",
+ "itself",
+ "ourselves",
+ "yourselves",
+ "themselves",
+ "this",
+ "that",
+ "these",
+ "those",
+ "who",
+ "whom",
+ "whose",
+ "which",
+ "what",
+ "who",
+ "whom",
+ "whose",
+ "which",
+ "that",
+ "all",
+ "another",
+ "any",
+ "anybody",
+ "anyone",
+ "anything",
+ "each",
+ "everybody",
+ "everyone",
+ "everything",
+ "few",
+ "many",
+ "nobody",
+ "none",
+ "one",
+ "several",
+ "some",
+ "somebody",
+ "someone",
+ "something",
+ "each other",
+ "one another",
+ "myself",
+ "yourself",
+ "himself",
+ "herself",
+ "itself",
+ "ourselves",
+ "yourselves",
+ "themselves",
+ "the image",
+ "image",
+ "images",
+ "the",
+ "a",
+ "an",
+ "a group",
+ "other objects",
+ "lots",
+ "a set",
+ ],
+ },
+ "pure_text": {},
+ "description_with_bboxes": {},
+ "description_with_polygons": {},
+ "polygons": {},
+ "bboxes": {},
+ "description_with_bboxes_or_polygons": {},
+ }
+ processor = Florence2Processor(
+ image_processor=image_processor, tokenizer=tokenizer, post_processor_config=post_processor_config
+ )
+
+ vision_config = convert_config(hf_config.vision_config.__dict__)
+ text_config = hf_config.text_config.__dict__
+ if text_config.get("model_type") == "florence2_language":
+ text_config["model_type"] = "bart"
+
+ config = Florence2Config(
+ text_config=text_config,
+ vision_config=vision_config,
+ image_token_id=tokenizer.image_token_id,
+ dtype=torch.float16,
+ )
+
+ for stage_idx in range(len(config.vision_config.embed_dim)):
+ list_of_state_dict = list_of_state_dict + vision_conv_embeddings(stage_idx)
+ for block_idx in range(config.vision_config.depths[stage_idx]):
+ list_of_state_dict = list_of_state_dict + vision_spatial_block(stage_idx, block_idx)
+ list_of_state_dict = list_of_state_dict + vision_channel_block(stage_idx, block_idx)
+
+ original_weights = hf_model.state_dict()
+ list_of_state_dict = list_of_state_dict + multi_modal_projector()
+ list_of_state_dict = list_of_state_dict + language_model(original_weights)
+ for i in range(len(list_of_state_dict)):
+ if list_of_state_dict[i][0] == "image_projection":
+ original_weights[list_of_state_dict[i][0]].transpose_(1, 0)
+ huggingface_weights[list_of_state_dict[i][1]] = original_weights[list_of_state_dict[i][0]]
+
+ model = Florence2ForConditionalGeneration(config)
+ model.load_state_dict(huggingface_weights, strict=True, assign=True)
+ model.tie_weights()
+ # We add an image token so we resize the model and pad to 64 for performance reasons
+ pad_shape = 64
+ model.resize_token_embeddings(len(tokenizer), pad_shape)
+
+ if pytorch_dump_folder:
+ model.save_pretrained(pytorch_dump_folder)
+ processor.save_pretrained(pytorch_dump_folder)
+
+ if output_hub_path:
+ model.push_to_hub(output_hub_path)
+ processor.push_to_hub(output_hub_path)
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "--hf_model_id",
+ default="microsoft/Florence-2-base",
+ type=str,
+ help="Name of the florence2 model you'd like to convert.",
+ )
+ parser.add_argument(
+ "--pytorch_dump_folder_path", default=None, type=str, help="Path to the output PyTorch model directory."
+ )
+ parser.add_argument(
+ "--output_hub_path",
+ help="Location on the hub of the converted model",
+ )
+
+ args = parser.parse_args()
+ convert_florence2_checkpoint(args.hf_model_id, args.pytorch_dump_folder_path, args.output_hub_path)
diff --git a/third_party/transformers/src/transformers/models/focalnet/__init__.py b/third_party/transformers/src/transformers/models/focalnet/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..5dec8135f3b3030b20691e761483e5994ba441f0
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/focalnet/__init__.py
@@ -0,0 +1,27 @@
+# Copyright 2024 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_focalnet import *
+ from .modeling_focalnet import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/focalnet/configuration_focalnet.py b/third_party/transformers/src/transformers/models/focalnet/configuration_focalnet.py
new file mode 100644
index 0000000000000000000000000000000000000000..c5bf658bbc2c3bbd60ac52508140d80022ea0aaf
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/focalnet/configuration_focalnet.py
@@ -0,0 +1,98 @@
+# Copyright 2023 The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""FocalNet model configuration"""
+
+from huggingface_hub.dataclasses import strict
+
+from ...backbone_utils import BackboneConfigMixin
+from ...configuration_utils import PreTrainedConfig
+from ...utils import auto_docstring
+
+
+@auto_docstring(checkpoint="microsoft/focalnet-tiny")
+@strict
+class FocalNetConfig(BackboneConfigMixin, PreTrainedConfig):
+ r"""
+ use_conv_embed (`bool`, *optional*, defaults to `False`):
+ Whether to use convolutional embedding. The authors noted that using convolutional embedding usually
+ improve the performance, but it's not used by default.
+ focal_levels (`list(int)`, *optional*, defaults to `[2, 2, 2, 2]`):
+ Number of focal levels in each layer of the respective stages in the encoder.
+ focal_windows (`list(int)`, *optional*, defaults to `[3, 3, 3, 3]`):
+ Focal window size in each layer of the respective stages in the encoder.
+ hidden_dropout_prob (`float`, *optional*, defaults to 0.0):
+ The dropout probability for all fully connected layers in the embeddings and encoder.
+ use_layerscale (`bool`, *optional*, defaults to `False`):
+ Whether to use layer scale in the encoder.
+ layerscale_value (`float`, *optional*, defaults to 0.0001):
+ The initial value of the layer scale.
+ use_post_layernorm (`bool`, *optional*, defaults to `False`):
+ Whether to use post layer normalization in the encoder.
+ use_post_layernorm_in_modulation (`bool`, *optional*, defaults to `False`):
+ Whether to use post layer normalization in the modulation layer.
+ normalize_modulator (`bool`, *optional*, defaults to `False`):
+ Whether to normalize the modulator.
+ encoder_stride (`int`, *optional*, defaults to 32):
+ Factor to increase the spatial resolution by in the decoder head for masked image modeling.
+
+ Example:
+
+ ```python
+ >>> from transformers import FocalNetConfig, FocalNetModel
+
+ >>> # Initializing a FocalNet microsoft/focalnet-tiny style configuration
+ >>> configuration = FocalNetConfig()
+
+ >>> # Initializing a model (with random weights) from the microsoft/focalnet-tiny style configuration
+ >>> model = FocalNetModel(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "focalnet"
+
+ image_size: int | list[int] | tuple[int, int] = 224
+ patch_size: int | list[int] | tuple[int, int] = 4
+ num_channels: int = 3
+ embed_dim: int = 96
+ use_conv_embed: bool = False
+ hidden_sizes: list[int] | tuple[int, ...] = (192, 384, 768, 768)
+ depths: list[int] | tuple[int, ...] = (2, 2, 6, 2)
+ focal_levels: list[int] | tuple[int, ...] = (2, 2, 2, 2)
+ focal_windows: list[int] | tuple[int, ...] = (3, 3, 3, 3)
+ hidden_act: str = "gelu"
+ mlp_ratio: float = 4.0
+ hidden_dropout_prob: float | int = 0.0
+ drop_path_rate: float | int = 0.1
+ use_layerscale: bool = False
+ layerscale_value: float = 1e-4
+ use_post_layernorm: bool = False
+ use_post_layernorm_in_modulation: bool = False
+ normalize_modulator: bool = False
+ initializer_range: float = 0.02
+ layer_norm_eps: float = 1e-5
+ encoder_stride: int = 32
+ _out_features: list[str] | None = None
+ _out_indices: list[int] | None = None
+
+ def __post_init__(self, **kwargs):
+ self.stage_names = ["stem"] + [f"stage{idx}" for idx in range(1, len(self.depths) + 1)]
+ self.set_output_features_output_indices(
+ out_indices=kwargs.pop("out_indices", None), out_features=kwargs.pop("out_features", None)
+ )
+ super().__post_init__(**kwargs)
+
+
+__all__ = ["FocalNetConfig"]
diff --git a/third_party/transformers/src/transformers/models/focalnet/convert_focalnet_to_hf_format.py b/third_party/transformers/src/transformers/models/focalnet/convert_focalnet_to_hf_format.py
new file mode 100644
index 0000000000000000000000000000000000000000..a8aac163189244ffc3605382ec367b2f0d8ffa5f
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/focalnet/convert_focalnet_to_hf_format.py
@@ -0,0 +1,239 @@
+# Copyright 2023 The HuggingFace Inc. team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Convert FocalNet checkpoints from the original repository. URL: https://github.com/microsoft/FocalNet/tree/main"""
+
+import argparse
+import json
+from io import BytesIO
+
+import httpx
+import torch
+from huggingface_hub import hf_hub_download
+from PIL import Image
+from torchvision import transforms
+
+from transformers import BitImageProcessor, FocalNetConfig, FocalNetForImageClassification
+from transformers.image_utils import IMAGENET_DEFAULT_MEAN, IMAGENET_DEFAULT_STD, PILImageResampling
+
+
+def get_focalnet_config(model_name):
+ depths = [2, 2, 6, 2] if "tiny" in model_name else [2, 2, 18, 2]
+ use_conv_embed = bool("large" in model_name or "huge" in model_name)
+ use_post_layernorm = bool("large" in model_name or "huge" in model_name)
+ use_layerscale = bool("large" in model_name or "huge" in model_name)
+
+ if "large" in model_name or "xlarge" in model_name or "huge" in model_name:
+ if "fl3" in model_name:
+ focal_levels = [3, 3, 3, 3]
+ focal_windows = [5, 5, 5, 5]
+ elif "fl4" in model_name:
+ focal_levels = [4, 4, 4, 4]
+ focal_windows = [3, 3, 3, 3]
+
+ if "tiny" in model_name or "small" in model_name or "base" in model_name:
+ focal_windows = [3, 3, 3, 3]
+ if "lrf" in model_name:
+ focal_levels = [3, 3, 3, 3]
+ else:
+ focal_levels = [2, 2, 2, 2]
+
+ if "tiny" in model_name:
+ embed_dim = 96
+ elif "small" in model_name:
+ embed_dim = 96
+ elif "base" in model_name:
+ embed_dim = 128
+ elif "large" in model_name:
+ embed_dim = 192
+ elif "xlarge" in model_name:
+ embed_dim = 256
+ elif "huge" in model_name:
+ embed_dim = 352
+
+ # set label information
+ repo_id = "huggingface/label-files"
+ if "large" in model_name or "huge" in model_name:
+ filename = "imagenet-22k-id2label.json"
+ else:
+ filename = "imagenet-1k-id2label.json"
+
+ id2label = json.load(open(hf_hub_download(repo_id, filename, repo_type="dataset"), "r"))
+ id2label = {int(k): v for k, v in id2label.items()}
+ label2id = {v: k for k, v in id2label.items()}
+
+ config = FocalNetConfig(
+ embed_dim=embed_dim,
+ depths=depths,
+ focal_levels=focal_levels,
+ focal_windows=focal_windows,
+ use_conv_embed=use_conv_embed,
+ id2label=id2label,
+ label2id=label2id,
+ use_post_layernorm=use_post_layernorm,
+ use_layerscale=use_layerscale,
+ )
+
+ return config
+
+
+def rename_key(name):
+ if "patch_embed.proj" in name:
+ name = name.replace("patch_embed.proj", "embeddings.patch_embeddings.projection")
+ if "patch_embed.norm" in name:
+ name = name.replace("patch_embed.norm", "embeddings.norm")
+ if "layers" in name:
+ name = "encoder." + name
+ if "encoder.layers" in name:
+ name = name.replace("encoder.layers", "encoder.stages")
+ if "downsample.proj" in name:
+ name = name.replace("downsample.proj", "downsample.projection")
+ if "blocks" in name:
+ name = name.replace("blocks", "layers")
+ if "modulation.f.weight" in name or "modulation.f.bias" in name:
+ name = name.replace("modulation.f", "modulation.projection_in")
+ if "modulation.h.weight" in name or "modulation.h.bias" in name:
+ name = name.replace("modulation.h", "modulation.projection_context")
+ if "modulation.proj.weight" in name or "modulation.proj.bias" in name:
+ name = name.replace("modulation.proj", "modulation.projection_out")
+
+ if name == "norm.weight":
+ name = "layernorm.weight"
+ if name == "norm.bias":
+ name = "layernorm.bias"
+
+ if "head" in name:
+ name = name.replace("head", "classifier")
+ else:
+ name = "focalnet." + name
+
+ return name
+
+
+def convert_focalnet_checkpoint(model_name, pytorch_dump_folder_path, push_to_hub=False):
+ # fmt: off
+ model_name_to_url = {
+ "focalnet-tiny": "https://projects4jw.blob.core.windows.net/focalnet/release/classification/focalnet_tiny_srf.pth",
+ "focalnet-tiny-lrf": "https://projects4jw.blob.core.windows.net/focalnet/release/classification/focalnet_tiny_lrf.pth",
+ "focalnet-small": "https://projects4jw.blob.core.windows.net/focalnet/release/classification/focalnet_small_srf.pth",
+ "focalnet-small-lrf": "https://projects4jw.blob.core.windows.net/focalnet/release/classification/focalnet_small_lrf.pth",
+ "focalnet-base": "https://projects4jw.blob.core.windows.net/focalnet/release/classification/focalnet_base_srf.pth",
+ "focalnet-base-lrf": "https://projects4jw.blob.core.windows.net/focalnet/release/classification/focalnet_base_lrf.pth",
+ "focalnet-large-lrf-fl3": "https://projects4jw.blob.core.windows.net/focalnet/release/classification/focalnet_large_lrf_384.pth",
+ "focalnet-large-lrf-fl4": "https://projects4jw.blob.core.windows.net/focalnet/release/classification/focalnet_large_lrf_384_fl4.pth",
+ "focalnet-xlarge-lrf-fl3": "https://projects4jw.blob.core.windows.net/focalnet/release/classification/focalnet_xlarge_lrf_384.pth",
+ "focalnet-xlarge-lrf-fl4": "https://projects4jw.blob.core.windows.net/focalnet/release/classification/focalnet_xlarge_lrf_384_fl4.pth",
+ }
+ # fmt: on
+
+ checkpoint_url = model_name_to_url[model_name]
+ print("Checkpoint URL: ", checkpoint_url)
+ state_dict = torch.hub.load_state_dict_from_url(checkpoint_url, map_location="cpu")["model"]
+
+ # rename keys
+ for key in state_dict.copy():
+ val = state_dict.pop(key)
+ state_dict[rename_key(key)] = val
+
+ config = get_focalnet_config(model_name)
+ model = FocalNetForImageClassification(config)
+ model.eval()
+
+ # load state dict
+ model.load_state_dict(state_dict)
+
+ # verify conversion
+ url = "http://images.cocodataset.org/val2017/000000039769.jpg"
+
+ with httpx.stream("GET", url) as response:
+ image = Image.open(BytesIO(response.read()))
+
+ processor = BitImageProcessor(
+ do_resize=True,
+ size={"shortest_edge": 256},
+ resample=PILImageResampling.BILINEAR,
+ do_center_crop=True,
+ crop_size=224,
+ do_normalize=True,
+ image_mean=IMAGENET_DEFAULT_MEAN,
+ image_std=IMAGENET_DEFAULT_STD,
+ )
+ inputs = processor(images=image, return_tensors="pt")
+
+ image_transforms = transforms.Compose(
+ [
+ transforms.Resize(256),
+ transforms.CenterCrop(224),
+ transforms.ToTensor(),
+ transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
+ ]
+ )
+
+ original_pixel_values = image_transforms(image).unsqueeze(0)
+
+ # verify pixel_values
+ assert torch.allclose(inputs.pixel_values, original_pixel_values, atol=1e-4)
+
+ outputs = model(**inputs)
+
+ predicted_class_idx = outputs.logits.argmax(-1).item()
+ print("Predicted class:", model.config.id2label[predicted_class_idx])
+
+ print("First values of logits:", outputs.logits[0, :3])
+
+ if model_name == "focalnet-tiny":
+ expected_slice = torch.tensor([0.2166, -0.4368, 0.2191])
+ elif model_name == "focalnet-tiny-lrf":
+ expected_slice = torch.tensor([1.1669, 0.0125, -0.1695])
+ elif model_name == "focalnet-small":
+ expected_slice = torch.tensor([0.4917, -0.0430, 0.1341])
+ elif model_name == "focalnet-small-lrf":
+ expected_slice = torch.tensor([-0.2588, -0.5342, -0.2331])
+ elif model_name == "focalnet-base":
+ expected_slice = torch.tensor([-0.1655, -0.4090, -0.1730])
+ elif model_name == "focalnet-base-lrf":
+ expected_slice = torch.tensor([0.5306, -0.0483, -0.3928])
+ assert torch.allclose(outputs.logits[0, :3], expected_slice, atol=1e-4)
+ print("Looks ok!")
+
+ if pytorch_dump_folder_path is not None:
+ print(f"Saving model and processor of {model_name} to {pytorch_dump_folder_path}")
+ model.save_pretrained(pytorch_dump_folder_path)
+ processor.save_pretrained(pytorch_dump_folder_path)
+
+ if push_to_hub:
+ print(f"Pushing model and processor of {model_name} to the hub...")
+ model.push_to_hub(f"{model_name}")
+ processor.push_to_hub(f"{model_name}")
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ # Required parameters
+ parser.add_argument(
+ "--model_name",
+ default="focalnet-tiny",
+ type=str,
+ help="Name of the FocalNet model you'd like to convert.",
+ )
+ parser.add_argument(
+ "--pytorch_dump_folder_path", default=None, type=str, help="Path to the output PyTorch model directory."
+ )
+ parser.add_argument(
+ "--push_to_hub",
+ action="store_true",
+ help="Whether to push the model and processor to the hub.",
+ )
+
+ args = parser.parse_args()
+ convert_focalnet_checkpoint(args.model_name, args.pytorch_dump_folder_path, args.push_to_hub)
diff --git a/third_party/transformers/src/transformers/models/focalnet/modeling_focalnet.py b/third_party/transformers/src/transformers/models/focalnet/modeling_focalnet.py
new file mode 100644
index 0000000000000000000000000000000000000000..b4e03c8884d53f022bbc69e69ce72d1915625273
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/focalnet/modeling_focalnet.py
@@ -0,0 +1,934 @@
+# Copyright 2023 Microsoft Research and The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""PyTorch FocalNet model."""
+
+import collections.abc
+import math
+from dataclasses import dataclass
+
+import torch
+from torch import nn
+
+from ... import initialization as init
+from ...activations import ACT2FN
+from ...backbone_utils import BackboneMixin, filter_output_hidden_states
+from ...modeling_layers import GradientCheckpointingLayer
+from ...modeling_outputs import BackboneOutput
+from ...modeling_utils import PreTrainedModel
+from ...utils import ModelOutput, auto_docstring, logging
+from ...utils.generic import can_return_tuple
+from .configuration_focalnet import FocalNetConfig
+
+
+logger = logging.get_logger(__name__)
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ FocalNet encoder's outputs, with potential hidden states.
+ """
+)
+class FocalNetEncoderOutput(ModelOutput):
+ r"""
+ reshaped_hidden_states (`tuple(torch.FloatTensor)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
+ Tuple of `torch.FloatTensor` (one for the output of the embeddings + one for the output of each stage) of
+ shape `(batch_size, hidden_size, height, width)`.
+
+ Hidden-states of the model at the output of each layer plus the initial embedding outputs reshaped to
+ include the spatial dimensions.
+ """
+
+ last_hidden_state: torch.FloatTensor | None = None
+ hidden_states: tuple[torch.FloatTensor] | None = None
+ reshaped_hidden_states: tuple[torch.FloatTensor] | None = None
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ FocalNet model's outputs that also contains a pooling of the last hidden states.
+ """
+)
+class FocalNetModelOutput(ModelOutput):
+ r"""
+ pooler_output (`torch.FloatTensor` of shape `(batch_size, hidden_size)`, *optional*, returned when `add_pooling_layer=True` is passed):
+ Average pooling of the last layer hidden-state.
+ reshaped_hidden_states (`tuple(torch.FloatTensor)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
+ Tuple of `torch.FloatTensor` (one for the output of the embeddings + one for the output of each stage) of
+ shape `(batch_size, hidden_size, height, width)`.
+
+ Hidden-states of the model at the output of each layer plus the initial embedding outputs reshaped to
+ include the spatial dimensions.
+ """
+
+ last_hidden_state: torch.FloatTensor | None = None
+ pooler_output: torch.FloatTensor | None = None
+ hidden_states: tuple[torch.FloatTensor] | None = None
+ reshaped_hidden_states: tuple[torch.FloatTensor] | None = None
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ FocalNet masked image model outputs.
+ """
+)
+class FocalNetMaskedImageModelingOutput(ModelOutput):
+ r"""
+ loss (`torch.FloatTensor` of shape `(1,)`, *optional*, returned when `bool_masked_pos` is provided):
+ Masked image modeling (MLM) loss.
+ reconstruction (`torch.FloatTensor` of shape `(batch_size, num_channels, height, width)`):
+ Reconstructed pixel values.
+ reshaped_hidden_states (`tuple(torch.FloatTensor)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
+ Tuple of `torch.FloatTensor` (one for the output of the embeddings + one for the output of each stage) of
+ shape `(batch_size, hidden_size, height, width)`.
+
+ Hidden-states of the model at the output of each layer plus the initial embedding outputs reshaped to
+ include the spatial dimensions.
+ """
+
+ loss: torch.FloatTensor | None = None
+ reconstruction: torch.FloatTensor | None = None
+ hidden_states: tuple[torch.FloatTensor] | None = None
+ reshaped_hidden_states: tuple[torch.FloatTensor] | None = None
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ FocalNet outputs for image classification.
+ """
+)
+class FocalNetImageClassifierOutput(ModelOutput):
+ r"""
+ loss (`torch.FloatTensor` of shape `(1,)`, *optional*, returned when `labels` is provided):
+ Classification (or regression if config.num_labels==1) loss.
+ logits (`torch.FloatTensor` of shape `(batch_size, config.num_labels)`):
+ Classification (or regression if config.num_labels==1) scores (before SoftMax).
+ reshaped_hidden_states (`tuple(torch.FloatTensor)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
+ Tuple of `torch.FloatTensor` (one for the output of the embeddings + one for the output of each stage) of
+ shape `(batch_size, hidden_size, height, width)`.
+
+ Hidden-states of the model at the output of each layer plus the initial embedding outputs reshaped to
+ include the spatial dimensions.
+ """
+
+ loss: torch.FloatTensor | None = None
+ logits: torch.FloatTensor | None = None
+ hidden_states: tuple[torch.FloatTensor] | None = None
+ reshaped_hidden_states: tuple[torch.FloatTensor] | None = None
+
+
+class FocalNetEmbeddings(nn.Module):
+ """
+ Construct the patch embeddings and layernorm. Optionally, also the mask token.
+ """
+
+ def __init__(self, config, use_mask_token=False):
+ super().__init__()
+
+ self.patch_embeddings = FocalNetPatchEmbeddings(
+ config=config,
+ image_size=config.image_size,
+ patch_size=config.patch_size,
+ num_channels=config.num_channels,
+ embed_dim=config.embed_dim,
+ use_conv_embed=config.use_conv_embed,
+ is_stem=True,
+ )
+ self.patch_grid = self.patch_embeddings.grid_size
+ self.mask_token = nn.Parameter(torch.zeros(1, 1, config.embed_dim)) if use_mask_token else None
+
+ self.norm = nn.LayerNorm(config.embed_dim, eps=config.layer_norm_eps)
+ self.dropout = nn.Dropout(config.hidden_dropout_prob)
+
+ def forward(
+ self, pixel_values: torch.FloatTensor | None, bool_masked_pos: torch.BoolTensor | None = None
+ ) -> tuple[torch.Tensor]:
+ embeddings, output_dimensions = self.patch_embeddings(pixel_values)
+ embeddings = self.norm(embeddings)
+ batch_size, seq_len, _ = embeddings.size()
+
+ if bool_masked_pos is not None:
+ mask_tokens = self.mask_token.expand(batch_size, seq_len, -1)
+ # replace the masked visual tokens by mask_tokens
+ mask = bool_masked_pos.unsqueeze(-1).type_as(mask_tokens)
+ embeddings = embeddings * (1.0 - mask) + mask_tokens * mask
+
+ embeddings = self.dropout(embeddings)
+ return embeddings, output_dimensions
+
+
+class FocalNetPatchEmbeddings(nn.Module):
+ def __init__(
+ self,
+ config,
+ image_size,
+ patch_size,
+ num_channels,
+ embed_dim,
+ add_norm=False,
+ use_conv_embed=False,
+ is_stem=False,
+ ):
+ super().__init__()
+ image_size = image_size if isinstance(image_size, collections.abc.Iterable) else (image_size, image_size)
+ patch_size = patch_size if isinstance(patch_size, collections.abc.Iterable) else (patch_size, patch_size)
+ num_patches = (image_size[1] // patch_size[1]) * (image_size[0] // patch_size[0])
+ self.image_size = image_size
+ self.patch_size = patch_size
+ self.num_channels = num_channels
+ self.num_patches = num_patches
+ self.grid_size = (image_size[0] // patch_size[0], image_size[1] // patch_size[1])
+
+ if use_conv_embed:
+ # if we choose to use conv embedding, then we treat the stem and non-stem differently
+ if is_stem:
+ kernel_size = 7
+ padding = 2
+ stride = 4
+ else:
+ kernel_size = 3
+ padding = 1
+ stride = 2
+ self.projection = nn.Conv2d(
+ num_channels, embed_dim, kernel_size=kernel_size, stride=stride, padding=padding
+ )
+ else:
+ self.projection = nn.Conv2d(num_channels, embed_dim, kernel_size=patch_size, stride=patch_size)
+
+ if add_norm:
+ self.norm = nn.LayerNorm(embed_dim, eps=config.layer_norm_eps)
+ else:
+ self.norm = None
+
+ def maybe_pad(self, pixel_values, height, width):
+ if width % self.patch_size[1] != 0:
+ pad_values = (0, self.patch_size[1] - width % self.patch_size[1])
+ pixel_values = nn.functional.pad(pixel_values, pad_values)
+ if height % self.patch_size[0] != 0:
+ pad_values = (0, 0, 0, self.patch_size[0] - height % self.patch_size[0])
+ pixel_values = nn.functional.pad(pixel_values, pad_values)
+ return pixel_values
+
+ def forward(self, pixel_values: torch.FloatTensor | None) -> tuple[torch.Tensor, tuple[int]]:
+ _, num_channels, height, width = pixel_values.shape
+ if num_channels != self.num_channels:
+ raise ValueError(
+ "Make sure that the channel dimension of the pixel values match with the one set in the configuration."
+ )
+ # pad the input to be divisible by self.patch_size, if needed
+ pixel_values = self.maybe_pad(pixel_values, height, width)
+ embeddings = self.projection(pixel_values)
+ _, _, height, width = embeddings.shape
+ output_dimensions = (height, width)
+ embeddings = embeddings.flatten(2).transpose(1, 2)
+
+ if self.norm is not None:
+ embeddings = self.norm(embeddings)
+
+ return embeddings, output_dimensions
+
+
+# Copied from transformers.models.beit.modeling_beit.drop_path
+def drop_path(input: torch.Tensor, drop_prob: float = 0.0, training: bool = False) -> torch.Tensor:
+ """
+ Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).
+
+ """
+ if drop_prob == 0.0 or not training:
+ return input
+ keep_prob = 1 - drop_prob
+ shape = (input.shape[0],) + (1,) * (input.ndim - 1) # work with diff dim tensors, not just 2D ConvNets
+ random_tensor = keep_prob + torch.rand(shape, dtype=input.dtype, device=input.device)
+ random_tensor.floor_() # binarize
+ output = input.div(keep_prob) * random_tensor
+ return output
+
+
+# Copied from transformers.models.beit.modeling_beit.BeitDropPath with Beit->FocalNet
+class FocalNetDropPath(nn.Module):
+ """Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks)."""
+
+ def __init__(self, drop_prob: float | None = None) -> None:
+ super().__init__()
+ self.drop_prob = drop_prob
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ return drop_path(hidden_states, self.drop_prob, self.training)
+
+ def extra_repr(self) -> str:
+ return f"p={self.drop_prob}"
+
+
+class FocalNetModulation(nn.Module):
+ def __init__(self, config, index, dim, focal_factor=2, bias=True, projection_dropout=0.0):
+ super().__init__()
+
+ self.dim = dim
+ self.focal_window = config.focal_windows[index]
+ self.focal_level = config.focal_levels[index]
+ self.focal_factor = focal_factor
+ self.use_post_layernorm_in_modulation = config.use_post_layernorm_in_modulation
+ self.normalize_modulator = config.normalize_modulator
+
+ self.projection_in = nn.Linear(dim, 2 * dim + (self.focal_level + 1), bias=bias)
+ self.projection_context = nn.Conv2d(dim, dim, kernel_size=1, stride=1, bias=bias)
+
+ self.activation = nn.GELU()
+ self.projection_out = nn.Linear(dim, dim)
+ self.projection_dropout = nn.Dropout(projection_dropout)
+ self.focal_layers = nn.ModuleList()
+
+ self.kernel_sizes = []
+ for k in range(self.focal_level):
+ kernel_size = self.focal_factor * k + self.focal_window
+ self.focal_layers.append(
+ nn.Sequential(
+ nn.Conv2d(
+ dim, dim, kernel_size=kernel_size, stride=1, groups=dim, padding=kernel_size // 2, bias=False
+ ),
+ nn.GELU(),
+ )
+ )
+ self.kernel_sizes.append(kernel_size)
+ if self.use_post_layernorm_in_modulation:
+ self.layernorm = nn.LayerNorm(dim, eps=config.layer_norm_eps)
+
+ def forward(self, hidden_state):
+ """
+ Args:
+ hidden_state:
+ Input features with shape of (batch_size, height, width, num_channels)
+ """
+ num_channels = hidden_state.shape[-1]
+
+ # pre linear projection
+ x = self.projection_in(hidden_state).permute(0, 3, 1, 2).contiguous()
+ q, ctx, gates = torch.split(x, (num_channels, num_channels, self.focal_level + 1), 1)
+
+ # context aggregation
+ ctx_all = 0
+ for level in range(self.focal_level):
+ ctx = self.focal_layers[level](ctx)
+ ctx_all = ctx_all + ctx * gates[:, level : level + 1]
+ ctx_global = self.activation(ctx.mean(2, keepdim=True).mean(3, keepdim=True))
+ ctx_all = ctx_all + ctx_global * gates[:, self.focal_level :]
+
+ # normalize context
+ if self.normalize_modulator:
+ ctx_all = ctx_all / (self.focal_level + 1)
+
+ # focal modulation
+ modulator = self.projection_context(ctx_all)
+ x_out = q * modulator
+ x_out = x_out.permute(0, 2, 3, 1).contiguous()
+ if self.use_post_layernorm_in_modulation:
+ x_out = self.layernorm(x_out)
+
+ # post linear projection
+ x_out = self.projection_out(x_out)
+ x_out = self.projection_dropout(x_out)
+ return x_out
+
+
+class FocalNetMlp(nn.Module):
+ def __init__(self, config, in_features, hidden_features=None, out_features=None, drop=0.0):
+ super().__init__()
+ out_features = out_features or in_features
+ hidden_features = hidden_features or in_features
+ self.fc1 = nn.Linear(in_features, hidden_features)
+ self.activation = ACT2FN[config.hidden_act]
+ self.fc2 = nn.Linear(hidden_features, out_features)
+ self.drop = nn.Dropout(drop)
+
+ def forward(self, hidden_state):
+ hidden_state = self.fc1(hidden_state)
+ hidden_state = self.activation(hidden_state)
+ hidden_state = self.drop(hidden_state)
+ hidden_state = self.fc2(hidden_state)
+ hidden_state = self.drop(hidden_state)
+ return hidden_state
+
+
+class FocalNetLayer(nn.Module):
+ r"""Focal Modulation Network layer (block).
+
+ Args:
+ config (`FocalNetConfig`):
+ Model config.
+ index (`int`):
+ Layer index.
+ dim (`int`):
+ Number of input channels.
+ input_resolution (`tuple[int]`):
+ Input resolution.
+ drop_path (`float`, *optional*, defaults to 0.0):
+ Stochastic depth rate.
+ """
+
+ def __init__(self, config, index, dim, input_resolution, drop_path=0.0):
+ super().__init__()
+
+ self.config = config
+
+ # layer-specific attributes
+ self.dim = dim
+ self.input_resolution = input_resolution
+
+ # general attributes
+ self.drop = config.hidden_dropout_prob
+ self.use_post_layernorm = config.use_post_layernorm
+
+ self.norm1 = nn.LayerNorm(dim, eps=config.layer_norm_eps)
+ self.modulation = FocalNetModulation(
+ config=config,
+ index=index,
+ dim=dim,
+ projection_dropout=self.drop,
+ )
+
+ self.drop_path = FocalNetDropPath(drop_path) if drop_path > 0.0 else nn.Identity()
+ self.norm2 = nn.LayerNorm(dim, eps=config.layer_norm_eps)
+ mlp_hidden_dim = int(dim * config.mlp_ratio)
+ self.mlp = FocalNetMlp(config=config, in_features=dim, hidden_features=mlp_hidden_dim, drop=self.drop)
+
+ self.gamma_1 = 1.0
+ self.gamma_2 = 1.0
+ if config.use_layerscale:
+ self.gamma_1 = nn.Parameter(config.layerscale_value * torch.ones(dim), requires_grad=True)
+ self.gamma_2 = nn.Parameter(config.layerscale_value * torch.ones(dim), requires_grad=True)
+
+ def forward(self, hidden_state, input_dimensions):
+ height, width = input_dimensions
+ batch_size, _, num_channels = hidden_state.shape
+ shortcut = hidden_state
+
+ # Focal Modulation
+ hidden_state = hidden_state if self.use_post_layernorm else self.norm1(hidden_state)
+ hidden_state = hidden_state.view(batch_size, height, width, num_channels)
+ hidden_state = self.modulation(hidden_state).view(batch_size, height * width, num_channels)
+ hidden_state = hidden_state if not self.use_post_layernorm else self.norm1(hidden_state)
+
+ # FFN
+ hidden_state = shortcut + self.drop_path(self.gamma_1 * hidden_state)
+ hidden_state = hidden_state + self.drop_path(
+ self.gamma_2
+ * (self.norm2(self.mlp(hidden_state)) if self.use_post_layernorm else self.mlp(self.norm2(hidden_state)))
+ )
+
+ return hidden_state
+
+
+class FocalNetStage(GradientCheckpointingLayer):
+ def __init__(self, config, index, input_resolution):
+ super().__init__()
+
+ self.config = config
+ self.num_stages = len(config.depths)
+
+ embed_dim = [config.embed_dim * (2**i) for i in range(self.num_stages)]
+ dim = embed_dim[index]
+ out_dim = embed_dim[index + 1] if (index < self.num_stages - 1) else None
+ downsample = FocalNetPatchEmbeddings if (index < self.num_stages - 1) else None
+
+ # stochastic depth decay rule
+ dpr = [x.item() for x in torch.linspace(0, config.drop_path_rate, sum(config.depths), device="cpu")]
+ drop_path = dpr[sum(config.depths[:index]) : sum(config.depths[: index + 1])]
+
+ self.layers = nn.ModuleList(
+ [
+ FocalNetLayer(
+ config=config,
+ index=index,
+ dim=dim,
+ input_resolution=input_resolution,
+ drop_path=drop_path[i] if isinstance(drop_path, list) else drop_path,
+ )
+ for i in range(config.depths[index])
+ ]
+ )
+
+ if downsample is not None:
+ self.downsample = downsample(
+ config=config,
+ image_size=input_resolution,
+ patch_size=2,
+ num_channels=dim,
+ embed_dim=out_dim,
+ add_norm=True,
+ use_conv_embed=config.use_conv_embed,
+ is_stem=False,
+ )
+ else:
+ self.downsample = None
+
+ self.pointing = False
+
+ def forward(self, hidden_states: torch.Tensor, input_dimensions: tuple[int, int]) -> tuple[torch.Tensor]:
+ height, width = input_dimensions
+ for layer_module in self.layers:
+ hidden_states = layer_module(hidden_states, input_dimensions)
+
+ hidden_states_before_downsampling = hidden_states
+ if self.downsample is not None:
+ height, width = input_dimensions
+ hidden_states = hidden_states.transpose(1, 2).reshape(
+ hidden_states_before_downsampling.shape[0], -1, height, width
+ )
+ hidden_states, output_dimensions = self.downsample(hidden_states)
+
+ else:
+ output_dimensions = (height, width, height, width)
+
+ stage_outputs = (hidden_states, hidden_states_before_downsampling, output_dimensions)
+
+ return stage_outputs
+
+
+class FocalNetEncoder(nn.Module):
+ def __init__(self, config, grid_size):
+ super().__init__()
+ self.num_stages = len(config.depths)
+ self.config = config
+
+ self.stages = nn.ModuleList(
+ [
+ FocalNetStage(
+ config=config,
+ index=i_layer,
+ input_resolution=(grid_size[0] // (2**i_layer), grid_size[1] // (2**i_layer)),
+ )
+ for i_layer in range(self.num_stages)
+ ]
+ )
+
+ self.gradient_checkpointing = False
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ input_dimensions: tuple[int, int],
+ output_hidden_states: bool | None = False,
+ output_hidden_states_before_downsampling: bool | None = False,
+ return_dict: bool | None = True,
+ ) -> tuple | FocalNetEncoderOutput:
+ all_hidden_states = () if output_hidden_states else None
+ all_reshaped_hidden_states = () if output_hidden_states else None
+
+ if output_hidden_states:
+ batch_size, _, hidden_size = hidden_states.shape
+ # rearrange b (h w) c -> b c h w
+ reshaped_hidden_state = hidden_states.view(batch_size, *input_dimensions, hidden_size)
+ reshaped_hidden_state = reshaped_hidden_state.permute(0, 3, 1, 2)
+ all_hidden_states += (hidden_states,)
+ all_reshaped_hidden_states += (reshaped_hidden_state,)
+
+ for i, stage_module in enumerate(self.stages):
+ stage_outputs = stage_module(hidden_states, input_dimensions)
+
+ hidden_states = stage_outputs[0]
+ hidden_states_before_downsampling = stage_outputs[1]
+ output_dimensions = stage_outputs[2]
+
+ input_dimensions = (output_dimensions[-2], output_dimensions[-1])
+
+ if output_hidden_states and output_hidden_states_before_downsampling:
+ batch_size, _, hidden_size = hidden_states_before_downsampling.shape
+ # rearrange b (h w) c -> b c h w
+ # here we use the original (not downsampled) height and width
+ reshaped_hidden_state = hidden_states_before_downsampling.view(
+ batch_size, *(output_dimensions[0], output_dimensions[1]), hidden_size
+ )
+ reshaped_hidden_state = reshaped_hidden_state.permute(0, 3, 1, 2)
+ all_hidden_states += (hidden_states_before_downsampling,)
+ all_reshaped_hidden_states += (reshaped_hidden_state,)
+ elif output_hidden_states and not output_hidden_states_before_downsampling:
+ batch_size, _, hidden_size = hidden_states.shape
+ # rearrange b (h w) c -> b c h w
+ reshaped_hidden_state = hidden_states.view(batch_size, *input_dimensions, hidden_size)
+ reshaped_hidden_state = reshaped_hidden_state.permute(0, 3, 1, 2)
+ all_hidden_states += (hidden_states,)
+ all_reshaped_hidden_states += (reshaped_hidden_state,)
+
+ if not return_dict:
+ return tuple(v for v in [hidden_states, all_hidden_states] if v is not None)
+
+ return FocalNetEncoderOutput(
+ last_hidden_state=hidden_states,
+ hidden_states=all_hidden_states,
+ reshaped_hidden_states=all_reshaped_hidden_states,
+ )
+
+
+@auto_docstring
+class FocalNetPreTrainedModel(PreTrainedModel):
+ config: FocalNetConfig
+ base_model_prefix = "focalnet"
+ main_input_name = "pixel_values"
+ supports_gradient_checkpointing = True
+ _no_split_modules = ["FocalNetStage"]
+
+ @torch.no_grad()
+ def _init_weights(self, module):
+ """Initialize the weights"""
+ super()._init_weights(module)
+ if isinstance(module, FocalNetEmbeddings):
+ if module.mask_token is not None:
+ init.zeros_(module.mask_token)
+ elif isinstance(module, FocalNetLayer):
+ if self.config.use_layerscale:
+ init.constant_(module.gamma_1, self.config.layerscale_value)
+ init.constant_(module.gamma_2, self.config.layerscale_value)
+
+
+@auto_docstring
+class FocalNetModel(FocalNetPreTrainedModel):
+ def __init__(self, config, add_pooling_layer=True, use_mask_token=False):
+ r"""
+ add_pooling_layer (bool, *optional*, defaults to `True`):
+ Whether to add a pooling layer
+ use_mask_token (`bool`, *optional*, defaults to `False`):
+ Whether to use a mask token for masked image modeling.
+ """
+ super().__init__(config)
+ self.config = config
+ self.num_stages = len(config.depths)
+ self.num_features = int(config.embed_dim * 2 ** (self.num_stages - 1))
+
+ self.embeddings = FocalNetEmbeddings(config, use_mask_token=use_mask_token)
+ self.encoder = FocalNetEncoder(config, self.embeddings.patch_grid)
+
+ self.layernorm = nn.LayerNorm(self.num_features, eps=config.layer_norm_eps)
+ self.pooler = nn.AdaptiveAvgPool1d(1) if add_pooling_layer else None
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ def get_input_embeddings(self):
+ return self.embeddings.patch_embeddings
+
+ @auto_docstring
+ def forward(
+ self,
+ pixel_values: torch.FloatTensor | None = None,
+ bool_masked_pos: torch.BoolTensor | None = None,
+ output_hidden_states: bool | None = None,
+ return_dict: bool | None = None,
+ **kwargs,
+ ) -> tuple | FocalNetModelOutput:
+ r"""
+ bool_masked_pos (`torch.BoolTensor` of shape `(batch_size, num_patches)`):
+ Boolean masked positions. Indicates which patches are masked (1) and which aren't (0).
+ """
+ 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.return_dict
+
+ if pixel_values is None:
+ raise ValueError("You have to specify pixel_values")
+
+ embedding_output, input_dimensions = self.embeddings(pixel_values, bool_masked_pos=bool_masked_pos)
+
+ encoder_outputs = self.encoder(
+ embedding_output,
+ input_dimensions,
+ output_hidden_states=output_hidden_states,
+ return_dict=return_dict,
+ )
+
+ sequence_output = encoder_outputs[0]
+ sequence_output = self.layernorm(sequence_output)
+
+ pooled_output = None
+ if self.pooler is not None:
+ pooled_output = self.pooler(sequence_output.transpose(1, 2))
+ pooled_output = torch.flatten(pooled_output, 1)
+
+ if not return_dict:
+ output = (sequence_output, pooled_output) + encoder_outputs[1:]
+
+ return output
+
+ return FocalNetModelOutput(
+ last_hidden_state=sequence_output,
+ pooler_output=pooled_output,
+ hidden_states=encoder_outputs.hidden_states,
+ reshaped_hidden_states=encoder_outputs.reshaped_hidden_states,
+ )
+
+
+@auto_docstring(
+ custom_intro="""
+ FocalNet Model with a decoder on top for masked image modeling.
+
+ This follows the same implementation as in [SimMIM](https://huggingface.co/papers/2111.09886).
+
+
+
+ Note that we provide a script to pre-train this model on custom data in our [examples
+ directory](https://github.com/huggingface/transformers/tree/main/examples/pytorch/image-pretraining).
+
+
+ """
+)
+class FocalNetForMaskedImageModeling(FocalNetPreTrainedModel):
+ def __init__(self, config):
+ super().__init__(config)
+
+ self.focalnet = FocalNetModel(config, add_pooling_layer=False, use_mask_token=True)
+
+ self.num_stages = len(config.depths)
+ num_features = int(config.embed_dim * 2 ** (self.num_stages - 1))
+ self.decoder = nn.Sequential(
+ nn.Conv2d(
+ in_channels=num_features, out_channels=config.encoder_stride**2 * config.num_channels, kernel_size=1
+ ),
+ nn.PixelShuffle(config.encoder_stride),
+ )
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @auto_docstring
+ def forward(
+ self,
+ pixel_values: torch.FloatTensor | None = None,
+ bool_masked_pos: torch.BoolTensor | None = None,
+ output_hidden_states: bool | None = None,
+ return_dict: bool | None = None,
+ **kwargs,
+ ) -> tuple | FocalNetMaskedImageModelingOutput:
+ r"""
+ bool_masked_pos (`torch.BoolTensor` of shape `(batch_size, num_patches)`):
+ Boolean masked positions. Indicates which patches are masked (1) and which aren't (0).
+
+ Examples:
+ ```python
+ >>> from transformers import AutoImageProcessor, FocalNetConfig, FocalNetForMaskedImageModeling
+ >>> import torch
+ >>> from PIL import Image
+ >>> import httpx
+ >>> from io import BytesIO
+
+ >>> url = "http://images.cocodataset.org/val2017/000000039769.jpg"
+ >>> with httpx.stream("GET", url) as response:
+ ... image = Image.open(BytesIO(response.read()))
+
+ >>> image_processor = AutoImageProcessor.from_pretrained("microsoft/focalnet-base-simmim-window6-192")
+ >>> config = FocalNetConfig()
+ >>> model = FocalNetForMaskedImageModeling(config)
+
+ >>> num_patches = (model.config.image_size // model.config.patch_size) ** 2
+ >>> pixel_values = image_processor(images=image, return_tensors="pt").pixel_values
+ >>> # create random boolean mask of shape (batch_size, num_patches)
+ >>> bool_masked_pos = torch.randint(low=0, high=2, size=(1, num_patches)).bool()
+
+ >>> outputs = model(pixel_values, bool_masked_pos=bool_masked_pos)
+ >>> loss, reconstructed_pixel_values = outputs.loss, outputs.logits
+ >>> list(reconstructed_pixel_values.shape)
+ [1, 3, 192, 192]
+ ```"""
+ return_dict = return_dict if return_dict is not None else self.config.return_dict
+
+ outputs = self.focalnet(
+ pixel_values,
+ bool_masked_pos=bool_masked_pos,
+ output_hidden_states=output_hidden_states,
+ return_dict=return_dict,
+ )
+
+ sequence_output = outputs[0]
+ # Reshape to (batch_size, num_channels, height, width)
+ sequence_output = sequence_output.transpose(1, 2)
+ batch_size, num_channels, sequence_length = sequence_output.shape
+ height = width = math.floor(sequence_length**0.5)
+ sequence_output = sequence_output.reshape(batch_size, num_channels, height, width)
+
+ # Reconstruct pixel values
+ reconstructed_pixel_values = self.decoder(sequence_output)
+
+ masked_im_loss = None
+ if bool_masked_pos is not None:
+ size = self.config.image_size // self.config.patch_size
+ bool_masked_pos = bool_masked_pos.reshape(-1, size, size)
+ mask = (
+ bool_masked_pos.repeat_interleave(self.config.patch_size, 1)
+ .repeat_interleave(self.config.patch_size, 2)
+ .unsqueeze(1)
+ .contiguous()
+ )
+ reconstruction_loss = nn.functional.l1_loss(pixel_values, reconstructed_pixel_values, reduction="none")
+ masked_im_loss = (reconstruction_loss * mask).sum() / (mask.sum() + 1e-5) / self.config.num_channels
+
+ if not return_dict:
+ output = (reconstructed_pixel_values,) + outputs[2:]
+ return ((masked_im_loss,) + output) if masked_im_loss is not None else output
+
+ return FocalNetMaskedImageModelingOutput(
+ loss=masked_im_loss,
+ reconstruction=reconstructed_pixel_values,
+ hidden_states=outputs.hidden_states,
+ reshaped_hidden_states=outputs.reshaped_hidden_states,
+ )
+
+
+@auto_docstring(
+ custom_intro="""
+ FocalNet Model with an image classification head on top (a linear layer on top of the pooled output) e.g. for
+ ImageNet.
+ """
+)
+class FocalNetForImageClassification(FocalNetPreTrainedModel):
+ # Copied from transformers.models.swin.modeling_swin.SwinForImageClassification.__init__ with Swin->FocalNet, swin->focalnet
+ def __init__(self, config):
+ super().__init__(config)
+
+ self.num_labels = config.num_labels
+ self.focalnet = FocalNetModel(config)
+
+ # Classifier head
+ self.classifier = (
+ nn.Linear(self.focalnet.num_features, config.num_labels) if config.num_labels > 0 else nn.Identity()
+ )
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @auto_docstring
+ def forward(
+ self,
+ pixel_values: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ output_hidden_states: bool | None = None,
+ return_dict: bool | None = None,
+ **kwargs,
+ ) -> tuple | FocalNetImageClassifierOutput:
+ r"""
+ labels (`torch.LongTensor` of shape `(batch_size,)`, *optional*):
+ Labels for computing the image classification/regression loss. Indices should be in `[0, ...,
+ config.num_labels - 1]`. If `config.num_labels == 1` a regression loss is computed (Mean-Square loss), If
+ `config.num_labels > 1` a classification loss is computed (Cross-Entropy).
+ """
+ return_dict = return_dict if return_dict is not None else self.config.return_dict
+
+ outputs = self.focalnet(
+ pixel_values,
+ output_hidden_states=output_hidden_states,
+ return_dict=return_dict,
+ )
+
+ pooled_output = outputs[1]
+
+ logits = self.classifier(pooled_output)
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(labels, logits, self.config)
+
+ if not return_dict:
+ output = (logits,) + outputs[2:]
+ return ((loss,) + output) if loss is not None else output
+
+ return FocalNetImageClassifierOutput(
+ loss=loss,
+ logits=logits,
+ hidden_states=outputs.hidden_states,
+ reshaped_hidden_states=outputs.reshaped_hidden_states,
+ )
+
+
+@auto_docstring(
+ custom_intro="""
+ FocalNet backbone, to be used with frameworks like X-Decoder.
+ """
+)
+class FocalNetBackbone(BackboneMixin, FocalNetPreTrainedModel):
+ has_attentions = False
+
+ def __init__(self, config: FocalNetConfig):
+ super().__init__(config)
+
+ self.num_features = [config.embed_dim] + config.hidden_sizes
+ self.focalnet = FocalNetModel(config)
+
+ # initialize weights and apply final processing
+ self.post_init()
+
+ @can_return_tuple
+ @filter_output_hidden_states
+ @auto_docstring
+ def forward(
+ self,
+ pixel_values: torch.Tensor,
+ output_hidden_states: bool | None = None,
+ return_dict: bool | None = None,
+ **kwargs,
+ ) -> BackboneOutput:
+ r"""
+ Examples:
+
+ ```python
+ >>> from transformers import AutoImageProcessor, AutoBackbone
+ >>> import torch
+ >>> from PIL import Image
+ >>> import httpx
+ >>> from io import BytesIO
+
+ >>> url = "http://images.cocodataset.org/val2017/000000039769.jpg"
+ >>> with httpx.stream("GET", url) as response:
+ ... image = Image.open(BytesIO(response.read()))
+
+ >>> processor = AutoImageProcessor.from_pretrained("microsoft/focalnet-tiny-lrf")
+ >>> model = AutoBackbone.from_pretrained("microsoft/focalnet-tiny-lrf")
+
+ >>> inputs = processor(image, return_tensors="pt")
+ >>> outputs = model(**inputs)
+ ```"""
+ return_dict = return_dict if return_dict is not None else self.config.return_dict
+ output_hidden_states = (
+ output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
+ )
+
+ outputs = self.focalnet(pixel_values, output_hidden_states=True, return_dict=True)
+
+ hidden_states = outputs.reshaped_hidden_states
+
+ feature_maps = ()
+ for idx, stage in enumerate(self.stage_names):
+ if stage in self.out_features:
+ feature_maps += (hidden_states[idx],)
+
+ if not return_dict:
+ output = (feature_maps,)
+ if output_hidden_states:
+ output += (outputs.hidden_states,)
+ return output
+
+ return BackboneOutput(
+ feature_maps=feature_maps,
+ hidden_states=outputs.hidden_states if output_hidden_states else None,
+ attentions=None,
+ )
+
+
+__all__ = [
+ "FocalNetForImageClassification",
+ "FocalNetForMaskedImageModeling",
+ "FocalNetBackbone",
+ "FocalNetModel",
+ "FocalNetPreTrainedModel",
+]
diff --git a/third_party/transformers/src/transformers/models/fsmt/__init__.py b/third_party/transformers/src/transformers/models/fsmt/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..f8f31762d681dbf3541d38c39fafdf5fa6b864d1
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/fsmt/__init__.py
@@ -0,0 +1,28 @@
+# Copyright 2024 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_fsmt import *
+ from .modeling_fsmt import *
+ from .tokenization_fsmt import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/fsmt/configuration_fsmt.py b/third_party/transformers/src/transformers/models/fsmt/configuration_fsmt.py
new file mode 100644
index 0000000000000000000000000000000000000000..bbc1fe601b8e522ee0ff737e68ab8850a9edd235
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/fsmt/configuration_fsmt.py
@@ -0,0 +1,108 @@
+# Copyright 2019-present, Facebook, Inc and the HuggingFace Inc. team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""FSMT configuration"""
+
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...utils import auto_docstring
+
+
+@auto_docstring(checkpoint="facebook/wmt19-en-ru")
+@strict
+class FSMTConfig(PreTrainedConfig):
+ r"""
+ langs (`list[str]`):
+ A list with source language and target_language (e.g., ['en', 'ru']).
+ src_vocab_size (`int`):
+ Vocabulary size of the encoder. Defines the number of different tokens that can be represented by the
+ `inputs_ids` passed to the forward method in the encoder.
+ tgt_vocab_size (`int`):
+ Vocabulary size of the decoder. Defines the number of different tokens that can be represented by the
+ `inputs_ids` passed to the forward method in the decoder.
+ max_length (`int`, *optional*, defaults to 200):
+ Maximum length to generate.
+ num_beams (`int`, *optional*, defaults to 5):
+ Number of beams for beam search that will be used by default in the `generate` method of the model. 1 means
+ no beam search.
+ length_penalty (`float`, *optional*, defaults to 1):
+ Exponential penalty to the length that is used with beam-based generation. It is applied as an exponent to
+ the sequence length, which in turn is used to divide the score of the sequence. Since the score is the log
+ likelihood of the sequence (i.e. negative), `length_penalty` > 0.0 promotes longer sequences, while
+ `length_penalty` < 0.0 encourages shorter sequences.
+ early_stopping (`bool`, *optional*, defaults to `False`):
+ Flag that will be used by default in the `generate` method of the model. Whether to stop the beam search
+ when at least `num_beams` sentences are finished per batch or not.
+
+ Examples:
+
+ ```python
+ >>> from transformers import FSMTConfig, FSMTModel
+
+ >>> # Initializing a FSMT facebook/wmt19-en-ru style configuration
+ >>> config = FSMTConfig()
+
+ >>> # Initializing a model (with random weights) from the configuration
+ >>> model = FSMTModel(config)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "fsmt"
+ attribute_map = {
+ "num_attention_heads": "encoder_attention_heads",
+ "hidden_size": "d_model",
+ "vocab_size": "tgt_vocab_size",
+ "num_hidden_layers": "encoder_layers",
+ }
+
+ langs: list[str] | tuple[str, ...] = ("en", "de")
+ src_vocab_size: int = 42024
+ tgt_vocab_size: int = 42024
+ activation_function: str = "relu"
+ d_model: int = 1024
+ max_length: int = 200
+ max_position_embeddings: int = 1024
+ encoder_ffn_dim: int = 4096
+ encoder_layers: int = 12
+ encoder_attention_heads: int = 16
+ encoder_layerdrop: float | int = 0.0
+ decoder_ffn_dim: int = 4096
+ decoder_layers: int = 12
+ decoder_attention_heads: int = 16
+ decoder_layerdrop: float | int = 0.0
+ attention_dropout: float | int = 0.0
+ dropout: float | int = 0.1
+ activation_dropout: float | int = 0.0
+ init_std: float = 0.02
+ decoder_start_token_id: int | None = 2
+ is_encoder_decoder: bool = True
+ scale_embedding: bool = True
+ tie_word_embeddings: bool = False
+ num_beams: int = 5
+ length_penalty: float = 1.0
+ early_stopping: bool = False
+ use_cache: bool = True
+ pad_token_id: int | None = 1
+ bos_token_id: int | None = 0
+ eos_token_id: int | list[int] | None = 2
+ forced_eos_token_id: int | list[int] | None = 2
+
+ def __post_init__(self, **kwargs):
+ kwargs.pop("decoder", None) # delete unused kwargs
+ super().__post_init__(**kwargs)
+
+
+__all__ = ["FSMTConfig"]
diff --git a/third_party/transformers/src/transformers/models/fsmt/convert_fsmt_original_pytorch_checkpoint_to_pytorch.py b/third_party/transformers/src/transformers/models/fsmt/convert_fsmt_original_pytorch_checkpoint_to_pytorch.py
new file mode 100644
index 0000000000000000000000000000000000000000..41223f41ce5e0d5c0200e149e9fdaf02dcb5b8eb
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/fsmt/convert_fsmt_original_pytorch_checkpoint_to_pytorch.py
@@ -0,0 +1,279 @@
+# Copyright 2018 The HuggingFace Inc. team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Note: if you intend to run this script make sure you look under scripts/fsmt/
+# to locate the appropriate script to do the work correctly. There is a set of scripts to:
+# - download and prepare data and run the conversion script
+# - perform eval to get the best hparam into the config
+# - generate model_cards - useful if you have multiple models from the same paper
+
+import argparse
+import json
+import os
+import re
+from collections import OrderedDict
+from os.path import basename, dirname
+
+import fairseq
+import torch
+from fairseq import hub_utils
+from fairseq.data.dictionary import Dictionary
+
+from transformers import FSMTConfig, FSMTForConditionalGeneration
+from transformers.models.fsmt.tokenization_fsmt import VOCAB_FILES_NAMES
+from transformers.tokenization_utils_base import TOKENIZER_CONFIG_FILE
+from transformers.utils import WEIGHTS_NAME, logging
+
+
+logging.set_verbosity_warning()
+
+json_indent = 2
+
+# based on the results of a search on a range of `num_beams`, `length_penalty` and `early_stopping`
+# values against wmt19 test data to obtain the best BLEU scores, we will use the following defaults:
+#
+# * `num_beams`: 5 (higher scores better, but requires more memory/is slower, can be adjusted by users)
+# * `early_stopping`: `False` consistently scored better
+# * `length_penalty` varied, so will assign the best one depending on the model
+best_score_hparams = {
+ # fairseq:
+ "wmt19-ru-en": {"length_penalty": 1.1},
+ "wmt19-en-ru": {"length_penalty": 1.15},
+ "wmt19-en-de": {"length_penalty": 1.0},
+ "wmt19-de-en": {"length_penalty": 1.1},
+ # allenai:
+ "wmt16-en-de-dist-12-1": {"length_penalty": 0.6},
+ "wmt16-en-de-dist-6-1": {"length_penalty": 0.6},
+ "wmt16-en-de-12-1": {"length_penalty": 0.8},
+ "wmt19-de-en-6-6-base": {"length_penalty": 0.6},
+ "wmt19-de-en-6-6-big": {"length_penalty": 0.6},
+}
+
+# this remaps the different models to their organization names
+org_names = {}
+for m in ["wmt19-ru-en", "wmt19-en-ru", "wmt19-en-de", "wmt19-de-en"]:
+ org_names[m] = "facebook"
+for m in [
+ "wmt16-en-de-dist-12-1",
+ "wmt16-en-de-dist-6-1",
+ "wmt16-en-de-12-1",
+ "wmt19-de-en-6-6-base",
+ "wmt19-de-en-6-6-big",
+]:
+ org_names[m] = "allenai"
+
+
+def rewrite_dict_keys(d):
+ # (1) remove word breaking symbol, (2) add word ending symbol where the word is not broken up,
+ # e.g.: d = {'le@@': 5, 'tt@@': 6, 'er': 7} => {'le': 5, 'tt': 6, 'er': 7}
+ d2 = dict((re.sub(r"@@$", "", k), v) if k.endswith("@@") else (re.sub(r"$", "", k), v) for k, v in d.items())
+ keep_keys = ["", "", "", ""]
+ # restore the special tokens
+ for k in keep_keys:
+ del d2[f"{k}"]
+ d2[k] = d[k] # restore
+ return d2
+
+
+def convert_fsmt_checkpoint_to_pytorch(fsmt_checkpoint_path, pytorch_dump_folder_path):
+ # prep
+ assert os.path.exists(fsmt_checkpoint_path)
+ os.makedirs(pytorch_dump_folder_path, exist_ok=True)
+ print(f"Writing results to {pytorch_dump_folder_path}")
+
+ # handle various types of models
+
+ checkpoint_file = basename(fsmt_checkpoint_path)
+ fsmt_folder_path = dirname(fsmt_checkpoint_path)
+
+ cls = fairseq.model_parallel.models.transformer.ModelParallelTransformerModel
+ models = cls.hub_models()
+ kwargs = {"bpe": "fastbpe", "tokenizer": "moses"}
+ data_name_or_path = "."
+ # note: since the model dump is old, fairseq has upgraded its model some
+ # time later, and it does a whole lot of rewrites and splits on the saved
+ # weights, therefore we can't use torch.load() directly on the model file.
+ # see: upgrade_state_dict(state_dict) in fairseq_model.py
+ print(f"using checkpoint {checkpoint_file}")
+ chkpt = hub_utils.from_pretrained(
+ fsmt_folder_path, checkpoint_file, data_name_or_path, archive_map=models, **kwargs
+ )
+
+ args = vars(chkpt["args"]["model"])
+
+ src_lang = args["source_lang"]
+ tgt_lang = args["target_lang"]
+
+ data_root = dirname(pytorch_dump_folder_path)
+ model_dir = basename(pytorch_dump_folder_path)
+
+ # dicts
+ src_dict_file = os.path.join(fsmt_folder_path, f"dict.{src_lang}.txt")
+ tgt_dict_file = os.path.join(fsmt_folder_path, f"dict.{tgt_lang}.txt")
+
+ src_dict = Dictionary.load(src_dict_file)
+ src_vocab = rewrite_dict_keys(src_dict.indices)
+ src_vocab_size = len(src_vocab)
+ src_vocab_file = os.path.join(pytorch_dump_folder_path, "vocab-src.json")
+ print(f"Generating {src_vocab_file} of {src_vocab_size} of {src_lang} records")
+ with open(src_vocab_file, "w", encoding="utf-8") as f:
+ f.write(json.dumps(src_vocab, ensure_ascii=False, indent=json_indent))
+
+ # detect whether this is a do_lower_case situation, which can be derived by checking whether we
+ # have at least one uppercase letter in the source vocab
+ do_lower_case = True
+ for k in src_vocab:
+ if not k.islower():
+ do_lower_case = False
+ break
+
+ tgt_dict = Dictionary.load(tgt_dict_file)
+ tgt_vocab = rewrite_dict_keys(tgt_dict.indices)
+ tgt_vocab_size = len(tgt_vocab)
+ tgt_vocab_file = os.path.join(pytorch_dump_folder_path, "vocab-tgt.json")
+ print(f"Generating {tgt_vocab_file} of {tgt_vocab_size} of {tgt_lang} records")
+ with open(tgt_vocab_file, "w", encoding="utf-8") as f:
+ f.write(json.dumps(tgt_vocab, ensure_ascii=False, indent=json_indent))
+
+ # merges_file (bpecodes)
+ merges_file = os.path.join(pytorch_dump_folder_path, VOCAB_FILES_NAMES["merges_file"])
+ for fn in ["bpecodes", "code"]: # older fairseq called the merges file "code"
+ fsmt_merges_file = os.path.join(fsmt_folder_path, fn)
+ if os.path.exists(fsmt_merges_file):
+ break
+ with open(fsmt_merges_file, encoding="utf-8") as fin:
+ merges = fin.read()
+ merges = re.sub(r" \d+$", "", merges, 0, re.MULTILINE) # remove frequency number
+ print(f"Generating {merges_file}")
+ with open(merges_file, "w", encoding="utf-8") as fout:
+ fout.write(merges)
+
+ # model config
+ fsmt_model_config_file = os.path.join(pytorch_dump_folder_path, "config.json")
+
+ # validate bpe/tokenizer config, as currently it's hardcoded to moses+fastbpe -
+ # may have to modify the tokenizer if a different type is used by a future model
+ assert args["bpe"] == "fastbpe", f"need to extend tokenizer to support bpe={args['bpe']}"
+ assert args["tokenizer"] == "moses", f"need to extend tokenizer to support bpe={args['tokenizer']}"
+
+ model_conf = {
+ "architectures": ["FSMTForConditionalGeneration"],
+ "model_type": "fsmt",
+ "activation_dropout": args["activation_dropout"],
+ "activation_function": "relu",
+ "attention_dropout": args["attention_dropout"],
+ "d_model": args["decoder_embed_dim"],
+ "dropout": args["dropout"],
+ "init_std": 0.02,
+ "max_position_embeddings": args["max_source_positions"],
+ "num_hidden_layers": args["encoder_layers"],
+ "src_vocab_size": src_vocab_size,
+ "tgt_vocab_size": tgt_vocab_size,
+ "langs": [src_lang, tgt_lang],
+ "encoder_attention_heads": args["encoder_attention_heads"],
+ "encoder_ffn_dim": args["encoder_ffn_embed_dim"],
+ "encoder_layerdrop": args["encoder_layerdrop"],
+ "encoder_layers": args["encoder_layers"],
+ "decoder_attention_heads": args["decoder_attention_heads"],
+ "decoder_ffn_dim": args["decoder_ffn_embed_dim"],
+ "decoder_layerdrop": args["decoder_layerdrop"],
+ "decoder_layers": args["decoder_layers"],
+ "bos_token_id": 0,
+ "pad_token_id": 1,
+ "eos_token_id": 2,
+ "is_encoder_decoder": True,
+ "scale_embedding": not args["no_scale_embedding"],
+ "tie_word_embeddings": args["share_all_embeddings"],
+ }
+
+ # good hparam defaults to start with
+ model_conf["num_beams"] = 5
+ model_conf["early_stopping"] = False
+ if model_dir in best_score_hparams and "length_penalty" in best_score_hparams[model_dir]:
+ model_conf["length_penalty"] = best_score_hparams[model_dir]["length_penalty"]
+ else:
+ model_conf["length_penalty"] = 1.0
+
+ print(f"Generating {fsmt_model_config_file}")
+ with open(fsmt_model_config_file, "w", encoding="utf-8") as f:
+ f.write(json.dumps(model_conf, ensure_ascii=False, indent=json_indent))
+
+ # tokenizer config
+ fsmt_tokenizer_config_file = os.path.join(pytorch_dump_folder_path, TOKENIZER_CONFIG_FILE)
+
+ tokenizer_conf = {
+ "langs": [src_lang, tgt_lang],
+ "model_max_length": 1024,
+ "do_lower_case": do_lower_case,
+ }
+
+ print(f"Generating {fsmt_tokenizer_config_file}")
+ with open(fsmt_tokenizer_config_file, "w", encoding="utf-8") as f:
+ f.write(json.dumps(tokenizer_conf, ensure_ascii=False, indent=json_indent))
+
+ # model
+ model = chkpt["models"][0]
+ model_state_dict = model.state_dict()
+
+ # rename keys to start with 'model.'
+ model_state_dict = OrderedDict(("model." + k, v) for k, v in model_state_dict.items())
+
+ # remove unneeded keys
+ ignore_keys = [
+ "model.model",
+ "model.encoder.version",
+ "model.decoder.version",
+ "model.encoder_embed_tokens.weight",
+ "model.decoder_embed_tokens.weight",
+ "model.encoder.embed_positions._float_tensor",
+ "model.decoder.embed_positions._float_tensor",
+ ]
+ for k in ignore_keys:
+ model_state_dict.pop(k, None)
+
+ config = FSMTConfig.from_pretrained(pytorch_dump_folder_path)
+ model_new = FSMTForConditionalGeneration(config)
+
+ # check that it loads ok
+ model_new.load_state_dict(model_state_dict, strict=False)
+
+ # save
+ pytorch_weights_dump_path = os.path.join(pytorch_dump_folder_path, WEIGHTS_NAME)
+ print(f"Generating {pytorch_weights_dump_path}")
+ torch.save(model_state_dict, pytorch_weights_dump_path)
+
+ print("Conversion is done!")
+ print("\nLast step is to upload the files to s3")
+ print(f"cd {data_root}")
+ print(f"transformers upload {model_dir}")
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ # Required parameters
+ parser.add_argument(
+ "--fsmt_checkpoint_path",
+ default=None,
+ type=str,
+ required=True,
+ help=(
+ "Path to the official PyTorch checkpoint file which is expected to reside in the dump dir with dicts,"
+ " bpecodes, etc."
+ ),
+ )
+ parser.add_argument(
+ "--pytorch_dump_folder_path", default=None, type=str, required=True, help="Path to the output PyTorch model."
+ )
+ args = parser.parse_args()
+ convert_fsmt_checkpoint_to_pytorch(args.fsmt_checkpoint_path, args.pytorch_dump_folder_path)
diff --git a/third_party/transformers/src/transformers/models/fsmt/modeling_fsmt.py b/third_party/transformers/src/transformers/models/fsmt/modeling_fsmt.py
new file mode 100644
index 0000000000000000000000000000000000000000..2d364266778d84de7c20b4e68a1fc52482bce0f3
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/fsmt/modeling_fsmt.py
@@ -0,0 +1,1136 @@
+# Copyright 2020 The Facebook AI Research Team Authors and The HuggingFace Inc. team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Original implementation: https://github.com/pytorch/fairseq/tree/master/examples/wmt19
+# Authors:
+# - @alexeib Alexei Baevski
+# - @edunov Sergey Edunov
+# - @michaelauli Michael Auli
+# - @myleott Myle Ott
+# - @nng555 Nathan Ng
+# - David Grangier
+# - Kyra Yee
+#
+# Paper: Facebook FAIR's WMT19 News Translation Task Submission https://huggingface.co/papers/1907.06616
+#
+"""PyTorch Fairseq model, ported from https://github.com/pytorch/fairseq/tree/master/examples/wmt19"""
+
+import math
+from typing import Any
+
+import torch
+from torch import Tensor, nn
+from torch.nn import CrossEntropyLoss, LayerNorm
+
+from ... import initialization as init
+from ...activations import ACT2FN
+from ...cache_utils import Cache, DynamicCache, EncoderDecoderCache
+from ...generation import GenerationMixin
+from ...modeling_outputs import (
+ BaseModelOutput,
+ BaseModelOutputWithPastAndCrossAttentions,
+ Seq2SeqLMOutput,
+ Seq2SeqModelOutput,
+)
+from ...modeling_utils import PreTrainedModel
+from ...utils import auto_docstring, logging
+from .configuration_fsmt import FSMTConfig
+
+
+logger = logging.get_logger(__name__)
+
+
+# See all FSMT models at https://huggingface.co/models?filter=fsmt
+
+# Porting notes:
+# this one is modeled after BartModel*
+#
+# Currently only translation (fairseq also has weights for LM)
+#
+# fairseq provides weights for ru-en, en-ru and de-en, en-de pairs. All have been ported.
+# - ru-en, en-ru use asymmetric vocab
+# - de-en, en-de use a merged single vocab (but the code works as if they are separate)
+#
+# Differences with Bart:
+# - not using bos token
+# - 2 separate vocabs (src and target)
+# - embed weights aren't tied
+# - uses a model Ensemble (but that part isn't ported/implemented yet) - so we
+# aren't getting as good of a BLEU score
+# - uses a projection layer at the end of the decoder
+# - doesn't use final_logits_bias
+# - beam search: stops as soon as num_beams == len(hypos) (whereas transformers
+# is not satisfied there and will continue searching until the next cycles
+# aren't promising something better), comparing BLEU scores - the transformers
+# algorithm is slightly superior, therefore using the latter. But if you want
+# to match fairseq outputs, you need to pass ``early_stopping=True`` to ``generate()``.
+#
+# SinusoidalPositionalEmbedding is slightly different from Bart's - generates
+# different embeddings. This implementation is copied verbatim from fairseq with
+# some small changes to make it work here.
+#
+# Other changes:
+# - doesn't support use_cache as Bart's version does
+#
+#
+# FSMTConfig changes with BartConfig
+#
+# Differences with BART:
+# - src/tgt vocabs aren't shared
+# - token embeddings aren't shared
+# - needs a language pair
+# - scale_embedding are True
+#
+# some unused args were removed too
+#
+#
+# TODO:
+# - port model ensemble (fs uses 4 model checkpoints)
+# - solve beam search discrepancies
+# docstyle-ignore
+
+"""
+
+Here is how to compare BLEU scores against fairseq implementation:
+(don't forget to install sacrebleu: `pip install sacrebleu`)
+
+# en-ru
+
+export PAIR=en-ru
+export DATA_DIR=data/$PAIR
+export SAVE_DIR=data/$PAIR
+export BS=8
+export NUM_BEAMS=50
+mkdir -p $DATA_DIR
+sacrebleu -t wmt19 -l $PAIR --echo src > $DATA_DIR/val.source
+sacrebleu -t wmt19 -l $PAIR --echo ref > $DATA_DIR/val.target
+echo $PAIR
+PYTHONPATH="src:examples/seq2seq" python examples/seq2seq/run_eval.py facebook/wmt19-$PAIR $DATA_DIR/val.source $SAVE_DIR/test_translations.txt --reference_path $DATA_DIR/val.target --score_path $SAVE_DIR/test_bleu.json --bs $BS --task translation --num_beams $NUM_BEAMS
+
+# (fairseq BLEU: 36.4 http://matrix.statmt.org/matrix/output/1914?score_id=37605)
+
+
+# ru-en
+
+export PAIR=ru-en
+export DATA_DIR=data/$PAIR
+export SAVE_DIR=data/$PAIR
+export BS=8
+export NUM_BEAMS=50
+mkdir -p $DATA_DIR
+sacrebleu -t wmt19 -l $PAIR --echo src > $DATA_DIR/val.source
+sacrebleu -t wmt19 -l $PAIR --echo ref > $DATA_DIR/val.target
+PYTHONPATH="src:examples/seq2seq" python examples/seq2seq/run_eval.py facebook/wmt19-$PAIR $DATA_DIR/val.source $SAVE_DIR/test_translations.txt --reference_path $DATA_DIR/val.target --score_path $SAVE_DIR/test_bleu.json --bs $BS --task translation --num_beams $NUM_BEAMS
+
+
+# (fairseq BLEU: 41.3 http://matrix.statmt.org/matrix/output/1907?run_id=6937)
+
+
+# de-en
+
+export PAIR=de-en
+export DATA_DIR=data/$PAIR
+export SAVE_DIR=data/$PAIR
+export BS=8
+export NUM_BEAMS=50
+mkdir -p $DATA_DIR
+sacrebleu -t wmt19 -l $PAIR --echo src > $DATA_DIR/val.source
+sacrebleu -t wmt19 -l $PAIR --echo ref > $DATA_DIR/val.target
+echo $PAIR
+PYTHONPATH="src:examples/seq2seq" python examples/seq2seq/run_eval.py facebook/wmt19-$PAIR $DATA_DIR/val.source $SAVE_DIR/test_translations.txt --reference_path $DATA_DIR/val.target --score_path $SAVE_DIR/test_bleu.json --bs $BS --task translation --num_beams $NUM_BEAMS
+
+# (fairseq BLEU: 42.3 http://matrix.statmt.org/matrix/output/1902?run_id=6750)
+
+
+
+# en-de
+
+export PAIR=en-de
+export DATA_DIR=data/$PAIR
+export SAVE_DIR=data/$PAIR
+export BS=8
+mkdir -p $DATA_DIR
+sacrebleu -t wmt19 -l $PAIR --echo src > $DATA_DIR/val.source
+sacrebleu -t wmt19 -l $PAIR --echo ref > $DATA_DIR/val.target
+echo $PAIR
+PYTHONPATH="src:examples/seq2seq" python examples/seq2seq/run_eval.py facebook/wmt19-$PAIR $DATA_DIR/val.source $SAVE_DIR/test_translations.txt --reference_path $DATA_DIR/val.target --score_path $SAVE_DIR/test_bleu.json --bs $BS --task translation --num_beams $NUM_BEAMS
+
+# (fairseq BLEU: 43.1 http://matrix.statmt.org/matrix/output/1909?run_id=6862)
+
+"""
+
+
+def invert_mask(attention_mask):
+ """Turns 1->0, 0->1, False->True, True-> False"""
+ assert attention_mask.dim() == 2
+ return attention_mask.eq(0)
+
+
+def triu_onnx(x, diagonal=0):
+ l = x.shape[0]
+ arange = torch.arange(l, device=x.device)
+ mask = arange.expand(l, l)
+ arange = arange.unsqueeze(-1)
+ if diagonal:
+ arange = arange + diagonal
+ mask = mask >= arange
+ return x.masked_fill(mask == 0, 0)
+
+
+def _prepare_fsmt_decoder_inputs(
+ config,
+ input_ids,
+ decoder_input_ids=None,
+ decoder_padding_mask=None,
+ causal_mask_dtype=torch.float32,
+):
+ """
+ Prepare masks that ignore padding tokens in the decoder and a causal mask for the decoder if none are provided.
+ This mimics the default behavior in fairseq. To override it pass in masks. Note: this is not called during
+ generation
+ """
+ pad_token_id = config.pad_token_id
+ if decoder_input_ids is None:
+ decoder_input_ids = shift_tokens_right(input_ids, pad_token_id)
+ bsz, tgt_len = decoder_input_ids.size()
+ if decoder_padding_mask is None:
+ decoder_padding_mask = make_padding_mask(decoder_input_ids, pad_token_id)
+ else:
+ decoder_padding_mask = invert_mask(decoder_padding_mask)
+ causal_mask = triu_onnx(fill_with_neg_inf(torch.zeros(tgt_len, tgt_len, dtype=causal_mask_dtype)), 1).to(
+ device=decoder_input_ids.device
+ )
+ return decoder_input_ids, decoder_padding_mask, causal_mask
+
+
+@auto_docstring
+class PretrainedFSMTModel(PreTrainedModel):
+ config: FSMTConfig
+ base_model_prefix = "model"
+
+ @torch.no_grad()
+ def _init_weights(self, module):
+ std = self.config.init_std
+ if isinstance(module, nn.Linear):
+ init.normal_(module.weight, mean=0.0, std=std)
+ if module.bias is not None:
+ init.zeros_(module.bias)
+ elif isinstance(module, SinusoidalPositionalEmbedding):
+ weight = module.get_embedding(*module.weight.shape, module.padding_idx)
+ init.copy_(module.weight, weight)
+ elif isinstance(module, nn.Embedding):
+ init.normal_(module.weight, mean=0.0, std=std)
+ # Here we need the check explicitly, as we slice the weight in the `zeros_` call, so it looses the flag
+ if module.padding_idx is not None and not getattr(module.weight, "_is_hf_initialized", False):
+ init.zeros_(module.weight[module.padding_idx])
+
+ @property
+ def dummy_inputs(self):
+ pad_token = self.config.pad_token_id
+ input_ids = torch.tensor([[0, 6, 10, 4, 2], [0, 8, 12, 2, pad_token]], device=self.device)
+ dummy_inputs = {
+ "attention_mask": input_ids.ne(pad_token),
+ "input_ids": input_ids,
+ }
+ return dummy_inputs
+
+
+def _make_linear_from_emb(emb):
+ vocab_size, emb_size = emb.weight.shape
+ lin_layer = nn.Linear(vocab_size, emb_size, bias=False)
+ lin_layer.weight.data = emb.weight.data
+ return lin_layer
+
+
+# Helper Functions, mostly for making masks
+def _check_shapes(shape_1, shape2):
+ if shape_1 != shape2:
+ raise AssertionError(f"shape mismatch: {shape_1} != {shape2}")
+
+
+def shift_tokens_right(input_ids, pad_token_id):
+ """Shift input ids one token to the right, and wrap the last non pad token (usually )."""
+
+ # replace possible -100 values in labels by `pad_token_id`
+ input_ids.masked_fill_(input_ids == -100, pad_token_id)
+
+ prev_output_tokens = input_ids.clone()
+ index_of_eos = (input_ids.ne(pad_token_id).sum(dim=1) - 1).unsqueeze(-1)
+ prev_output_tokens[:, 0] = input_ids.gather(1, index_of_eos).squeeze()
+ prev_output_tokens[:, 1:] = input_ids[:, :-1]
+ return prev_output_tokens
+
+
+def make_padding_mask(input_ids, padding_idx=1):
+ """True for pad tokens"""
+ padding_mask = input_ids.eq(padding_idx)
+ if not padding_mask.any():
+ padding_mask = None
+ return padding_mask
+
+
+# Helper Modules
+
+
+class EncoderLayer(nn.Module):
+ def __init__(self, config: FSMTConfig):
+ super().__init__()
+ self.embed_dim = config.d_model
+ self.self_attn = Attention(self.embed_dim, config.encoder_attention_heads, dropout=config.attention_dropout)
+ self.self_attn_layer_norm = LayerNorm(self.embed_dim)
+ self.dropout = config.dropout
+ self.activation_fn = ACT2FN[config.activation_function]
+ self.activation_dropout = config.activation_dropout
+ self.fc1 = nn.Linear(self.embed_dim, config.encoder_ffn_dim)
+ self.fc2 = nn.Linear(config.encoder_ffn_dim, self.embed_dim)
+ self.final_layer_norm = LayerNorm(self.embed_dim)
+
+ def forward(self, x, encoder_padding_mask, output_attentions=False):
+ """
+ Args:
+ x (`torch.Tensor`): input to the layer of shape *(seq_len, batch, embed_dim)*
+ encoder_padding_mask (`torch.ByteTensor`): binary ByteTensor of shape
+ *(batch, src_len)* where padding elements are indicated by `1`.
+ for t_tgt, t_src is excluded (or masked out), =0 means it is
+ included in attention
+
+ Returns:
+ encoded output of shape *(seq_len, batch, embed_dim)*
+ """
+ residual = x
+ x, attn_weights = self.self_attn(
+ query=x,
+ key=x,
+ key_padding_mask=encoder_padding_mask,
+ output_attentions=output_attentions,
+ )
+ x = nn.functional.dropout(x, p=self.dropout, training=self.training)
+ x = residual + x
+ x = self.self_attn_layer_norm(x)
+
+ residual = x
+ x = self.activation_fn(self.fc1(x))
+ x = nn.functional.dropout(x, p=self.activation_dropout, training=self.training)
+ x = self.fc2(x)
+ x = nn.functional.dropout(x, p=self.dropout, training=self.training)
+ x = residual + x
+ x = self.final_layer_norm(x)
+ return x, attn_weights
+
+
+class FSMTEncoder(nn.Module):
+ """
+ Transformer encoder consisting of *config.encoder_layers* self attention layers. Each layer is a [`EncoderLayer`].
+
+ Args:
+ config: FSMTConfig
+ """
+
+ def __init__(self, config: FSMTConfig):
+ super().__init__()
+ self.dropout = config.dropout
+ self.layerdrop = config.encoder_layerdrop
+ self.padding_idx = config.pad_token_id
+ self.embed_tokens = nn.Embedding(config.src_vocab_size, config.d_model, config.pad_token_id)
+ embed_dim = self.embed_tokens.embedding_dim
+ self.embed_scale = math.sqrt(embed_dim) if config.scale_embedding else 1.0
+ self.embed_positions = SinusoidalPositionalEmbedding(
+ config.max_position_embeddings + self.padding_idx + 1, embed_dim, self.padding_idx
+ )
+ self.layers = nn.ModuleList([EncoderLayer(config) for _ in range(config.encoder_layers)]) # type: list[EncoderLayer]
+
+ def forward(
+ self,
+ input_ids: torch.Tensor,
+ attention_mask: torch.Tensor | None = None,
+ inputs_embeds: torch.Tensor | None = None,
+ output_attentions: bool = False,
+ output_hidden_states: bool = False,
+ return_dict: bool = True,
+ ):
+ """
+ Args:
+ input_ids (`torch.LongTensor`): tokens in the source language of shape
+ *(batch, src_len)*
+ attention_mask (`torch.LongTensor`): indicating which indices are padding tokens
+ inputs_embeds (`torch.FloatTensor`):
+ embedding vectors of shape *(batch, src_len, embed_dim)*
+
+ Returns:
+ BaseModelOutput or Tuple comprised of:
+
+ - **x** (`torch.Tensor`): the last encoder layer's output of shape *(src_len, batch, embed_dim)*
+ - **encoder_states** (`Tuple(torch.FloatTensor)`): all intermediate hidden states of shape *(src_len,
+ batch, embed_dim)*. Only populated if *output_hidden_states:* is True.
+ - **all_attentions** (`Tuple(torch.FloatTensor)`): Attention weights for each layer.
+ During training might not be of length n_layers because of layer dropout.
+ """
+ # check attention mask and invert
+ if attention_mask is not None:
+ attention_mask = invert_mask(attention_mask)
+
+ if input_ids is not None and inputs_embeds is not None:
+ raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time")
+ elif input_ids is not None:
+ inputs_embeds = self.embed_tokens(input_ids) * self.embed_scale
+ embed_pos = self.embed_positions(input_ids)
+ elif inputs_embeds is not None:
+ inputs_embeds = inputs_embeds * self.embed_scale
+
+ # We assume zeros hidden states correspond to padding tokens
+ # and create `position_ids` where inputs_embeds[:, :, 0] == 0
+ position_ids = inputs_embeds[:, :, 0].masked_fill(
+ inputs_embeds[:, :, 0].eq(0), self.embed_positions.padding_idx
+ )
+
+ embed_pos = self.embed_positions(position_ids)
+ else:
+ raise ValueError("You have to specify either input_ids or inputs_embeds")
+
+ x = inputs_embeds + embed_pos
+ x = nn.functional.dropout(x, p=self.dropout, training=self.training)
+
+ # B x T x C -> T x B x C
+ x = x.transpose(0, 1)
+
+ encoder_states = () if output_hidden_states else None
+ all_attentions = () if output_attentions else None
+ for idx, encoder_layer in enumerate(self.layers):
+ if output_hidden_states:
+ x = x.transpose(0, 1) # T x B x C -> B x T x C
+ encoder_states += (x,)
+ x = x.transpose(0, 1) # B x T x C -> T x B x C
+ # add LayerDrop (see https://huggingface.co/papers/1909.11556 for description)
+ dropout_probability = torch.rand([])
+ if self.training and (dropout_probability < self.layerdrop): # skip the layer
+ attn = None
+ else:
+ x, attn = encoder_layer(
+ x,
+ attention_mask,
+ output_attentions=output_attentions,
+ )
+
+ if output_attentions:
+ all_attentions = all_attentions + (attn,)
+
+ # T x B x C -> B x T x C
+ x = x.transpose(0, 1)
+
+ if output_hidden_states:
+ encoder_states += (x,)
+
+ if not return_dict:
+ return tuple(v for v in [x, encoder_states, all_attentions] if v is not None)
+ return BaseModelOutput(last_hidden_state=x, hidden_states=encoder_states, attentions=all_attentions)
+
+
+class DecoderLayer(nn.Module):
+ def __init__(self, config: FSMTConfig, layer_idx=None):
+ super().__init__()
+ self.embed_dim = config.d_model
+
+ self.self_attn = Attention(
+ embed_dim=self.embed_dim,
+ num_heads=config.decoder_attention_heads,
+ dropout=config.attention_dropout,
+ layer_idx=layer_idx,
+ )
+ self.dropout = config.dropout
+ self.activation_fn = ACT2FN[config.activation_function]
+ self.activation_dropout = config.activation_dropout
+
+ self.self_attn_layer_norm = LayerNorm(self.embed_dim)
+ self.encoder_attn = Attention(
+ self.embed_dim,
+ config.decoder_attention_heads,
+ dropout=config.attention_dropout,
+ encoder_decoder_attention=True,
+ layer_idx=layer_idx,
+ )
+ self.encoder_attn_layer_norm = LayerNorm(self.embed_dim)
+ self.fc1 = nn.Linear(self.embed_dim, config.decoder_ffn_dim)
+ self.fc2 = nn.Linear(config.decoder_ffn_dim, self.embed_dim)
+ self.final_layer_norm = LayerNorm(self.embed_dim)
+
+ def forward(
+ self,
+ x,
+ encoder_hidden_states,
+ encoder_attn_mask=None,
+ layer_state=None,
+ causal_mask=None,
+ decoder_padding_mask=None,
+ output_attentions=False,
+ **kwargs,
+ ):
+ residual = x
+
+ # Self Attention
+ x, self_attn_weights = self.self_attn(
+ query=x,
+ key=x,
+ layer_state=layer_state, # adds keys to layer state
+ key_padding_mask=decoder_padding_mask,
+ attn_mask=causal_mask,
+ output_attentions=output_attentions,
+ )
+ x = nn.functional.dropout(x, p=self.dropout, training=self.training)
+ x = residual + x
+ x = self.self_attn_layer_norm(x)
+
+ # Cross attention
+ residual = x
+ assert self.encoder_attn.cache_key != self.self_attn.cache_key
+ x, cross_attn_weights = self.encoder_attn(
+ query=x,
+ key=encoder_hidden_states,
+ key_padding_mask=encoder_attn_mask,
+ layer_state=layer_state, # mutates layer state
+ output_attentions=output_attentions,
+ )
+ x = nn.functional.dropout(x, p=self.dropout, training=self.training)
+ x = residual + x
+ x = self.encoder_attn_layer_norm(x)
+
+ # Fully Connected
+ residual = x
+ x = self.activation_fn(self.fc1(x))
+ x = nn.functional.dropout(x, p=self.activation_dropout, training=self.training)
+ x = self.fc2(x)
+ x = nn.functional.dropout(x, p=self.dropout, training=self.training)
+ x = residual + x
+ x = self.final_layer_norm(x)
+ return (
+ x,
+ self_attn_weights,
+ cross_attn_weights,
+ )
+
+
+class FSMTDecoder(nn.Module):
+ """
+ Transformer decoder consisting of *config.decoder_layers* layers. Each layer is a [`DecoderLayer`]
+
+ Args:
+ config: FSMTConfig
+ embed_tokens (nn.Embedding): output embedding
+ """
+
+ def __init__(self, config: FSMTConfig):
+ super().__init__()
+ self.dropout = config.dropout
+ self.layerdrop = config.decoder_layerdrop
+ self.padding_idx = config.pad_token_id
+ self.embed_scale = math.sqrt(config.d_model) if config.scale_embedding else 1.0
+ self.embed_tokens = nn.Embedding(config.tgt_vocab_size, config.d_model, self.padding_idx)
+ embed_dim = self.embed_tokens.embedding_dim
+ self.embed_positions = SinusoidalPositionalEmbedding(
+ config.max_position_embeddings + self.padding_idx + 1, embed_dim, self.padding_idx
+ )
+ self.layers = nn.ModuleList([DecoderLayer(config, layer_idx=i) for i in range(config.decoder_layers)]) # type: list[DecoderLayer]
+ self.output_projection = nn.Linear(config.d_model, config.tgt_vocab_size, bias=False)
+
+ def forward(
+ self,
+ input_ids: torch.Tensor,
+ encoder_hidden_states: torch.Tensor,
+ encoder_padding_mask: torch.Tensor,
+ decoder_padding_mask: torch.Tensor,
+ decoder_causal_mask: torch.Tensor,
+ inputs_embeds: torch.Tensor | None = None,
+ past_key_values: Cache | None = None,
+ use_cache: bool | None = False,
+ output_attentions: bool | None = False,
+ output_hidden_states: bool | None = False,
+ return_dict: bool | None = True,
+ **kwargs,
+ ):
+ """
+ Includes several features from "Jointly Learning to Align and Translate with Transformer Models" (Garg et al.,
+ EMNLP 2019).
+
+ Args:
+ input_ids (`torch.LongTensor` of shape `(batch, tgt_len)`):
+ previous decoder outputs for teacher forcing
+ encoder_hidden_states: output from the encoder, used for
+ encoder-side attention
+ encoder_padding_mask: for ignoring pad tokens
+ past_key_values (dict or None): dictionary used for storing state during generation
+
+ Returns:
+ BaseModelOutputWithPast or tuple:
+
+ - the decoder's features of shape *(batch, tgt_len, embed_dim)*
+ - the cache
+ - hidden states
+ - attentions
+ """
+ # check attention mask and invert
+ if encoder_padding_mask is not None:
+ encoder_padding_mask = invert_mask(encoder_padding_mask)
+
+ if input_ids is not None and inputs_embeds is not None:
+ raise ValueError("You cannot specify both decoder_input_ids and decoder_inputs_embeds at the same time")
+ elif input_ids is not None:
+ # embed positions
+ positions = self.embed_positions(input_ids)
+ if use_cache:
+ input_ids = input_ids[:, -1:]
+ positions = positions[:, -1:] # happens after we embed them
+ x = self.embed_tokens(input_ids) * self.embed_scale
+ elif inputs_embeds is not None:
+ # We assume zeros hidden states correspond to padding tokens
+ # and create `position_ids` where inputs_embeds[:, :, 0] == 0
+ position_ids = inputs_embeds[:, :, 0].masked_fill(
+ inputs_embeds[:, :, 0].eq(0), self.embed_positions.padding_idx
+ )
+ positions = self.embed_positions(position_ids)
+ x = inputs_embeds * self.embed_scale
+ else:
+ raise ValueError("You have to specify either decoder_input_ids or decoder_inputs_embeds")
+
+ x += positions
+ x = nn.functional.dropout(x, p=self.dropout, training=self.training)
+
+ # Convert to FSMT output format: (BS, seq_len, model_dim) -> (seq_len, BS, model_dim)
+ x = x.transpose(0, 1)
+ encoder_hidden_states = encoder_hidden_states.transpose(0, 1)
+
+ # decoder layers
+ all_hidden_states = () if output_hidden_states else None
+ all_self_attns = () if output_attentions else None
+ all_cross_attns = () if output_attentions else None
+
+ for idx, decoder_layer in enumerate(self.layers):
+ # add LayerDrop (see https://huggingface.co/papers/1909.11556 for description)
+ if output_hidden_states:
+ x = x.transpose(0, 1)
+ all_hidden_states += (x,)
+ x = x.transpose(0, 1)
+ if self.training:
+ dropout_probability = torch.rand([])
+ if dropout_probability < self.layerdrop:
+ continue
+
+ x, layer_self_attn, layer_cross_attn = decoder_layer(
+ x,
+ encoder_hidden_states,
+ encoder_attn_mask=encoder_padding_mask,
+ decoder_padding_mask=decoder_padding_mask,
+ layer_state=past_key_values,
+ causal_mask=decoder_causal_mask,
+ output_attentions=output_attentions,
+ )
+
+ if output_attentions:
+ all_self_attns += (layer_self_attn,)
+ all_cross_attns += (layer_cross_attn,)
+
+ # add hidden states from the last decoder layer
+ if output_hidden_states:
+ x = x.transpose(0, 1)
+ all_hidden_states += (x,)
+ x = x.transpose(0, 1)
+
+ # Convert to standard output format: (seq_len, BS, model_dim) -> (BS, seq_len, model_dim)
+ x = x.transpose(0, 1)
+ encoder_hidden_states = encoder_hidden_states.transpose(0, 1)
+
+ x = self.output_projection(x)
+
+ if not return_dict:
+ return tuple(
+ v for v in [x, past_key_values, all_hidden_states, all_self_attns, all_cross_attns] if v is not None
+ )
+ return BaseModelOutputWithPastAndCrossAttentions(
+ last_hidden_state=x,
+ past_key_values=past_key_values,
+ hidden_states=all_hidden_states,
+ attentions=all_self_attns,
+ cross_attentions=all_cross_attns,
+ )
+
+
+def _reorder_buffer(attn_cache, new_order):
+ for k, input_buffer_k in attn_cache.items():
+ if input_buffer_k is not None:
+ attn_cache[k] = input_buffer_k.index_select(0, new_order)
+ return attn_cache
+
+
+class Attention(nn.Module):
+ """Multi-headed attention from 'Attention Is All You Need' paper"""
+
+ def __init__(
+ self,
+ embed_dim,
+ num_heads,
+ dropout=0.0,
+ bias=True,
+ encoder_decoder_attention=False, # otherwise self_attention
+ layer_idx=None,
+ ):
+ super().__init__()
+ self.embed_dim = embed_dim
+ self.num_heads = num_heads
+ self.dropout = dropout
+ self.head_dim = embed_dim // num_heads
+ assert self.head_dim * num_heads == self.embed_dim, "embed_dim must be divisible by num_heads"
+ self.scaling = self.head_dim**-0.5
+ self.layer_idx = layer_idx
+
+ self.encoder_decoder_attention = encoder_decoder_attention
+ self.k_proj = nn.Linear(embed_dim, embed_dim, bias=bias)
+ self.v_proj = nn.Linear(embed_dim, embed_dim, bias=bias)
+ self.q_proj = nn.Linear(embed_dim, embed_dim, bias=bias)
+ self.out_proj = nn.Linear(embed_dim, embed_dim, bias=bias)
+ self.cache_key = "encoder_decoder" if self.encoder_decoder_attention else "self"
+
+ def forward(
+ self,
+ query,
+ key: Tensor | None,
+ key_padding_mask: Tensor | None = None,
+ layer_state: Cache | None = None,
+ attn_mask: Tensor | None = None,
+ output_attentions: bool | None = False,
+ **kwargs,
+ ) -> tuple[Tensor, Tensor | None]:
+ """Input shape: Time(SeqLen) x Batch x Channel"""
+ tgt_len, bsz, embed_dim = query.size()
+ assert embed_dim == self.embed_dim
+ assert list(query.size()) == [tgt_len, bsz, embed_dim]
+
+ if layer_state is not None:
+ if isinstance(layer_state, EncoderDecoderCache):
+ is_updated = layer_state.is_updated.get(self.layer_idx)
+ if self.encoder_decoder_attention:
+ # after the first generated id, we can subsequently re-use all key/value_states from cache
+ curr_past_key_values = layer_state.cross_attention_cache
+ else:
+ curr_past_key_values = layer_state.self_attention_cache
+ else:
+ curr_past_key_values = layer_state
+
+ # NOTE: FSMT has format (seq_len, BS, model_dim) for inputs
+ current_states = key if self.encoder_decoder_attention else query
+ if self.encoder_decoder_attention and layer_state is not None and is_updated:
+ # reuse k,v, cross_attentions
+ key_states = curr_past_key_values.layers[self.layer_idx].keys
+ value_states = curr_past_key_values.layers[self.layer_idx].values
+ else:
+ key_states = self.k_proj(current_states)
+ value_states = self.v_proj(current_states)
+ key_states = key_states.view(-1, bsz, self.num_heads, self.head_dim).permute(1, 2, 0, 3)
+ value_states = value_states.view(-1, bsz, self.num_heads, self.head_dim).permute(1, 2, 0, 3)
+
+ if layer_state is not None:
+ # save all key/value_states to cache to be re-used for fast auto-regressive generation
+ key_states, value_states = curr_past_key_values.update(key_states, value_states, self.layer_idx)
+ # set flag that curr layer for cross-attn is already updated so we can re-use in subsequent calls
+ if self.encoder_decoder_attention:
+ layer_state.is_updated[self.layer_idx] = True
+
+ query_states = self.q_proj(query) * self.scaling
+
+ # Reshape back to 3D tensors for `bmm`
+ query_states = query_states.view(-1, bsz * self.num_heads, self.head_dim).transpose(0, 1)
+ key_states = key_states.reshape(bsz * self.num_heads, -1, self.head_dim)
+ value_states = value_states.reshape(bsz * self.num_heads, -1, self.head_dim)
+
+ assert key_states is not None
+ src_len = key_states.size(1)
+ attn_weights = torch.bmm(query_states, key_states.transpose(1, 2))
+ assert attn_weights.size() == (bsz * self.num_heads, tgt_len, src_len)
+
+ if attn_mask is not None:
+ attn_weights = attn_weights.view(bsz, self.num_heads, tgt_len, src_len) + attn_mask
+ attn_weights = attn_weights.view(bsz * self.num_heads, tgt_len, src_len)
+
+ # This is part of a workaround to get around fork/join parallelism not supporting Optional types.
+ if key_padding_mask is not None and key_padding_mask.dim() == 0:
+ key_padding_mask = None
+ assert key_padding_mask is None or key_padding_mask.size()[:2] == (
+ bsz,
+ src_len,
+ )
+
+ if key_padding_mask is not None: # don't attend to padding symbols
+ attn_weights = attn_weights.view(bsz, self.num_heads, tgt_len, src_len)
+ reshaped = key_padding_mask.unsqueeze(1).unsqueeze(2)
+ attn_weights = attn_weights.masked_fill(reshaped, torch.finfo(attn_weights.dtype).min)
+ attn_weights = attn_weights.view(bsz * self.num_heads, tgt_len, src_len)
+
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1)
+
+ if output_attentions:
+ # make sure that attn_weights are included in graph
+ attn_weights_reshaped = attn_weights.view(bsz, self.num_heads, tgt_len, src_len)
+ attn_weights = attn_weights_reshaped.view(bsz * self.num_heads, tgt_len, src_len)
+ else:
+ attn_weights_reshaped = None
+
+ attn_probs = nn.functional.dropout(
+ attn_weights,
+ p=self.dropout,
+ training=self.training,
+ )
+
+ assert value_states is not None
+ attn_output = torch.bmm(attn_probs, value_states)
+ assert attn_output.size() == (bsz * self.num_heads, tgt_len, self.head_dim)
+ attn_output = attn_output.transpose(0, 1).contiguous().view(tgt_len, bsz, embed_dim)
+ attn_output = self.out_proj(attn_output)
+
+ return attn_output, attn_weights_reshaped
+
+
+def fill_with_neg_inf(t):
+ """FP16-compatible function that fills a input_ids with -inf."""
+ return t.float().fill_(torch.finfo(t.dtype).min).type_as(t)
+
+
+# Public API
+def _get_shape(t):
+ return getattr(t, "shape", None)
+
+
+@auto_docstring
+class FSMTModel(PretrainedFSMTModel):
+ _tied_weights_keys = {
+ "encoder.embed_tokens.weight": "decoder.embed_tokens.weight",
+ "decoder.output_projection.weight": "decoder.embed_tokens.weight",
+ }
+
+ def __init__(self, config: FSMTConfig):
+ super().__init__(config)
+ self.encoder = FSMTEncoder(config)
+ self.decoder = FSMTDecoder(config)
+ self.post_init()
+
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor,
+ attention_mask: torch.Tensor | None = None,
+ decoder_input_ids: torch.LongTensor | None = None,
+ decoder_attention_mask: torch.BoolTensor | None = None,
+ encoder_outputs: tuple[torch.FloatTensor] | None = None,
+ past_key_values: Cache | None = None,
+ use_cache: bool | None = None,
+ output_attentions: bool | None = None,
+ output_hidden_states: bool | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ decoder_inputs_embeds: torch.FloatTensor | None = None,
+ return_dict: bool | None = None,
+ **kwargs,
+ ) -> tuple[torch.Tensor] | Seq2SeqModelOutput:
+ r"""
+ decoder_input_ids (`torch.LongTensor` of shape `(batch_size, target_sequence_length)`, *optional*):
+ Indices of decoder input sequence tokens in the vocabulary.
+
+ Indices can be obtained using [`AutoTokenizer`]. See [`PreTrainedTokenizer.encode`] and
+ [`PreTrainedTokenizer.__call__`] for details.
+
+ [What are decoder input IDs?](../glossary#decoder-input-ids)
+
+ FSMT uses the `eos_token_id` as the starting token for `decoder_input_ids` generation. If `past_key_values`
+ is used, optionally only the last `decoder_input_ids` have to be input (see `past_key_values`).
+ decoder_attention_mask (`torch.BoolTensor` of shape `(batch_size, target_sequence_length)`, *optional*):
+ Default behavior: generate a tensor that ignores pad tokens in `decoder_input_ids`. Causal mask will also
+ be used by default.
+ """
+ if decoder_input_ids is None:
+ use_cache = False
+
+ 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
+ )
+ use_cache = use_cache if use_cache is not None else self.config.use_cache
+ return_dict = return_dict if return_dict is not None else self.config.return_dict
+
+ # make masks if user doesn't supply
+ if not use_cache and input_ids is not None:
+ decoder_input_ids, decoder_padding_mask, causal_mask = _prepare_fsmt_decoder_inputs(
+ self.config,
+ input_ids,
+ decoder_input_ids=decoder_input_ids,
+ decoder_padding_mask=decoder_attention_mask,
+ causal_mask_dtype=self.decoder.embed_tokens.weight.dtype,
+ )
+ else:
+ decoder_padding_mask, causal_mask = None, None
+
+ if decoder_input_ids is None and decoder_inputs_embeds is None:
+ raise ValueError("Make sure that `decoder_input_ids` or `decoder_inputs_embeds` are passed.")
+
+ if use_cache and past_key_values is None:
+ past_key_values = EncoderDecoderCache(DynamicCache(config=self.config), DynamicCache(config=self.config))
+
+ if encoder_outputs is None:
+ encoder_outputs = self.encoder(
+ input_ids=input_ids,
+ attention_mask=attention_mask,
+ inputs_embeds=inputs_embeds,
+ output_attentions=output_attentions,
+ output_hidden_states=output_hidden_states,
+ return_dict=return_dict,
+ )
+ # If the user passed a tuple for encoder_outputs, we wrap it in a BaseModelOutput when return_dict=False
+ elif return_dict and not isinstance(encoder_outputs, BaseModelOutput):
+ encoder_outputs = BaseModelOutput(
+ last_hidden_state=encoder_outputs[0],
+ hidden_states=encoder_outputs[1] if len(encoder_outputs) > 1 else None,
+ attentions=encoder_outputs[2] if len(encoder_outputs) > 2 else None,
+ )
+
+ # decoder outputs consists of (dec_features, layer_state, dec_hidden, dec_attn)
+ decoder_outputs = self.decoder(
+ decoder_input_ids,
+ encoder_outputs[0],
+ attention_mask,
+ decoder_padding_mask,
+ decoder_causal_mask=causal_mask,
+ inputs_embeds=decoder_inputs_embeds,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ output_attentions=output_attentions,
+ output_hidden_states=output_hidden_states,
+ return_dict=return_dict,
+ )
+
+ if not return_dict:
+ return decoder_outputs + encoder_outputs
+
+ return Seq2SeqModelOutput(
+ last_hidden_state=decoder_outputs.last_hidden_state,
+ past_key_values=decoder_outputs.past_key_values,
+ decoder_hidden_states=decoder_outputs.hidden_states,
+ decoder_attentions=decoder_outputs.attentions,
+ cross_attentions=decoder_outputs.cross_attentions,
+ encoder_last_hidden_state=encoder_outputs.last_hidden_state,
+ encoder_hidden_states=encoder_outputs.hidden_states,
+ encoder_attentions=encoder_outputs.attentions,
+ )
+
+ def get_input_embeddings(self):
+ return self.encoder.embed_tokens
+
+ def set_input_embeddings(self, value):
+ self.encoder.embed_tokens = value
+
+ def get_output_embeddings(self):
+ return self.decoder.embed_tokens
+
+ def set_output_embeddings(self, value):
+ self.decoder.embed_tokens = value
+
+
+@auto_docstring(
+ custom_intro="""
+ The FSMT Model with a language modeling head. Can be used for summarization.
+ """
+)
+class FSMTForConditionalGeneration(PretrainedFSMTModel, GenerationMixin):
+ base_model_prefix = "model"
+
+ def __init__(self, config: FSMTConfig):
+ super().__init__(config)
+ base_model = FSMTModel(config)
+ self.model = base_model
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ decoder_input_ids: torch.LongTensor | None = None,
+ decoder_attention_mask: torch.BoolTensor | None = None,
+ encoder_outputs: tuple[torch.FloatTensor] | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.Tensor | None = None,
+ decoder_inputs_embeds: torch.Tensor | None = None,
+ labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ output_attentions: bool | None = None,
+ output_hidden_states: bool | None = None,
+ return_dict: bool | None = None,
+ **kwargs,
+ ) -> tuple[torch.Tensor] | Seq2SeqLMOutput:
+ r"""
+ decoder_input_ids (`torch.LongTensor` of shape `(batch_size, target_sequence_length)`, *optional*):
+ Indices of decoder input sequence tokens in the vocabulary.
+
+ Indices can be obtained using [`AutoTokenizer`]. See [`PreTrainedTokenizer.encode`] and
+ [`PreTrainedTokenizer.__call__`] for details.
+
+ [What are decoder input IDs?](../glossary#decoder-input-ids)
+
+ FSMT uses the `eos_token_id` as the starting token for `decoder_input_ids` generation. If `past_key_values`
+ is used, optionally only the last `decoder_input_ids` have to be input (see `past_key_values`).
+ decoder_attention_mask (`torch.BoolTensor` of shape `(batch_size, target_sequence_length)`, *optional*):
+ Default behavior: generate a tensor that ignores pad tokens in `decoder_input_ids`. Causal mask will also
+ be used by default.
+ 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]`.
+
+ Example Translation:
+
+ ```python
+ >>> from transformers import AutoTokenizer, FSMTForConditionalGeneration
+
+ >>> mname = "facebook/wmt19-ru-en"
+ >>> model = FSMTForConditionalGeneration.from_pretrained(mname)
+ >>> tokenizer = AutoTokenizer.from_pretrained(mname)
+
+ >>> src_text = "Машинное обучение - это здорово, не так ли?"
+ >>> input_ids = tokenizer(src_text, return_tensors="pt").input_ids
+ >>> outputs = model.generate(input_ids, num_beams=5, num_return_sequences=3)
+ >>> tokenizer.decode(outputs[0], skip_special_tokens=True)
+ "Machine learning is great, isn't it?"
+ ```
+ """
+ return_dict = return_dict if return_dict is not None else self.config.return_dict
+
+ if labels is not None:
+ use_cache = False
+
+ outputs = self.model(
+ input_ids,
+ inputs_embeds=inputs_embeds,
+ attention_mask=attention_mask,
+ decoder_input_ids=decoder_input_ids,
+ decoder_inputs_embeds=decoder_inputs_embeds,
+ encoder_outputs=encoder_outputs,
+ decoder_attention_mask=decoder_attention_mask,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ output_attentions=output_attentions,
+ output_hidden_states=output_hidden_states,
+ return_dict=return_dict,
+ )
+ lm_logits = outputs[0]
+
+ masked_lm_loss = None
+ if labels is not None:
+ loss_fct = CrossEntropyLoss()
+ # TODO(SS): do we need to ignore pad tokens in labels?
+ masked_lm_loss = loss_fct(lm_logits.view(-1, self.config.tgt_vocab_size), labels.view(-1))
+
+ if not return_dict:
+ output = (lm_logits,) + outputs[1:]
+ return ((masked_lm_loss,) + output) if masked_lm_loss is not None else output
+
+ return Seq2SeqLMOutput(
+ loss=masked_lm_loss,
+ logits=lm_logits,
+ past_key_values=outputs.past_key_values,
+ decoder_hidden_states=outputs.decoder_hidden_states,
+ decoder_attentions=outputs.decoder_attentions,
+ cross_attentions=outputs.cross_attentions,
+ encoder_last_hidden_state=outputs.encoder_last_hidden_state,
+ encoder_hidden_states=outputs.encoder_hidden_states,
+ encoder_attentions=outputs.encoder_attentions,
+ )
+
+ def prepare_decoder_input_ids_from_labels(self, labels: torch.Tensor):
+ return shift_tokens_right(labels, self.config.pad_token_id)
+
+ def get_output_embeddings(self):
+ return self.model.decoder.embed_tokens
+
+ def set_output_embeddings(self, value):
+ self.model.decoder.embed_tokens = value
+
+
+class SinusoidalPositionalEmbedding(nn.Embedding):
+ """
+ This module produces sinusoidal positional embeddings of any length.
+
+ We don't want to save the weight of this embedding since it's not trained (deterministic) and it can be huge.
+
+ Padding symbols are ignored.
+
+ These embeddings get automatically extended in forward if more positions is needed.
+ """
+
+ def __init__(self, num_positions, embedding_dim, padding_idx):
+ super().__init__(num_positions, embedding_dim, padding_idx)
+
+ def make_weight(self, num_positions, embedding_dim, padding_idx):
+ weight = self.get_embedding(num_positions, embedding_dim, padding_idx)
+ # in forward put the weights on the correct dtype and device of the param
+ weight = weight.to(dtype=self.weight.dtype, device=self.weight.device)
+ self.weight = nn.Parameter(weight)
+ self.weight.detach_()
+ self.weight.requires_grad = False
+
+ @staticmethod
+ def get_embedding(num_embeddings, embedding_dim, padding_idx):
+ """
+ Build sinusoidal embeddings.
+
+ This matches the implementation in tensor2tensor, but differs slightly from the description in Section 3.5 of
+ "Attention Is All You Need".
+ """
+ half_dim = embedding_dim // 2
+ emb = math.log(10000) / (half_dim - 1)
+ emb = torch.exp(torch.arange(half_dim, dtype=torch.int64).float() * -emb)
+ emb = torch.arange(num_embeddings, dtype=torch.int64).float().unsqueeze(1) * emb.unsqueeze(0)
+ emb = torch.cat([torch.sin(emb), torch.cos(emb)], dim=1).view(num_embeddings, -1)
+ if embedding_dim % 2 == 1:
+ # zero pad
+ emb = torch.cat([emb, torch.zeros(num_embeddings, 1)], dim=1)
+ if padding_idx is not None:
+ emb[padding_idx, :] = 0
+ return emb
+
+ @staticmethod
+ def make_positions(tensor, padding_idx: int):
+ """
+ Replace non-padding symbols with their position numbers.
+
+ Position numbers begin at padding_idx+1. Padding symbols are ignored.
+ """
+ # The series of casts and type-conversions here are carefully
+ # balanced to both work with ONNX export and XLA. In particular XLA
+ # prefers ints, cumsum defaults to output longs, and ONNX doesn't know
+ # how to handle the dtype kwarg in cumsum.
+ mask = tensor.ne(padding_idx).int()
+ return (torch.cumsum(mask, dim=1).type_as(mask) * mask).long() + padding_idx
+
+ def forward(
+ self,
+ input,
+ incremental_state: Any | None = None,
+ timestep: Tensor | None = None,
+ ):
+ """Input is expected to be of size [bsz x seqlen]."""
+ bsz, seq_len = input.shape[:2]
+ max_pos = self.padding_idx + 1 + seq_len
+ if max_pos > self.weight.size(0):
+ # expand embeddings if needed
+ self.make_weight(max_pos, self.embedding_dim, self.padding_idx)
+ positions = self.make_positions(input, self.padding_idx)
+ return super().forward(positions)
+
+
+__all__ = ["FSMTForConditionalGeneration", "FSMTModel", "PretrainedFSMTModel"]
diff --git a/third_party/transformers/src/transformers/models/fsmt/tokenization_fsmt.py b/third_party/transformers/src/transformers/models/fsmt/tokenization_fsmt.py
new file mode 100644
index 0000000000000000000000000000000000000000..40eeb1fb855b7352c7ad7f2bed4ea626f14aa8b8
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/fsmt/tokenization_fsmt.py
@@ -0,0 +1,486 @@
+# Copyright 2019 The Open AI Team Authors and The HuggingFace Inc. team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Tokenization classes for FSMT."""
+
+import json
+import os
+import re
+import unicodedata
+
+from ...tokenization_python import PreTrainedTokenizer
+from ...utils import logging
+
+
+logger = logging.get_logger(__name__)
+
+VOCAB_FILES_NAMES = {
+ "src_vocab_file": "vocab-src.json",
+ "tgt_vocab_file": "vocab-tgt.json",
+ "merges_file": "merges.txt",
+}
+
+
+def get_pairs(word):
+ """
+ Return set of symbol pairs in a word. word is represented as tuple of symbols (symbols being variable-length
+ strings)
+ """
+ pairs = set()
+ prev_char = word[0]
+ for char in word[1:]:
+ pairs.add((prev_char, char))
+ prev_char = char
+ return pairs
+
+
+def replace_unicode_punct(text):
+ """
+ Port of https://github.com/moses-smt/mosesdecoder/blob/master/scripts/tokenizer/replace-unicode-punctuation.perl
+ """
+ text = text.replace(",", ",")
+ text = re.sub(r"。\s*", ". ", text)
+ text = text.replace("、", ",")
+ text = text.replace("”", '"')
+ text = text.replace("“", '"')
+ text = text.replace("∶", ":")
+ text = text.replace(":", ":")
+ text = text.replace("?", "?")
+ text = text.replace("《", '"')
+ text = text.replace("》", '"')
+ text = text.replace(")", ")")
+ text = text.replace("!", "!")
+ text = text.replace("(", "(")
+ text = text.replace(";", ";")
+ text = text.replace("1", "1")
+ text = text.replace("」", '"')
+ text = text.replace("「", '"')
+ text = text.replace("0", "0")
+ text = text.replace("3", "3")
+ text = text.replace("2", "2")
+ text = text.replace("5", "5")
+ text = text.replace("6", "6")
+ text = text.replace("9", "9")
+ text = text.replace("7", "7")
+ text = text.replace("8", "8")
+ text = text.replace("4", "4")
+ text = re.sub(r".\s*", ". ", text)
+ text = text.replace("~", "~")
+ text = text.replace("’", "'")
+ text = text.replace("…", "...")
+ text = text.replace("━", "-")
+ text = text.replace("〈", "<")
+ text = text.replace("〉", ">")
+ text = text.replace("【", "[")
+ text = text.replace("】", "]")
+ text = text.replace("%", "%")
+ return text
+
+
+def remove_non_printing_char(text):
+ """
+ Port of https://github.com/moses-smt/mosesdecoder/blob/master/scripts/tokenizer/remove-non-printing-char.perl
+ """
+ output = []
+ for char in text:
+ cat = unicodedata.category(char)
+ if cat.startswith("C"):
+ continue
+ output.append(char)
+ return "".join(output)
+
+
+# Porting notes:
+# this one is modeled after XLMTokenizer
+#
+# added:
+# - src_vocab_file,
+# - tgt_vocab_file,
+# - langs,
+
+
+class FSMTTokenizer(PreTrainedTokenizer):
+ """
+ Construct an FAIRSEQ Transformer tokenizer. Based on Byte-Pair Encoding. The tokenization process is the following:
+
+ - Moses preprocessing and tokenization.
+ - Normalizing all inputs text.
+ - The arguments `special_tokens` and the function `set_special_tokens`, can be used to add additional symbols (like
+ "__classify__") to a vocabulary.
+ - The argument `langs` defines a pair of languages.
+
+ This tokenizer inherits from [`PreTrainedTokenizer`] which contains most of the main methods. Users should refer to
+ this superclass for more information regarding those methods.
+
+ Args:
+ langs (`List[str]`, *optional*):
+ A list of two languages to translate from and to, for instance `["en", "ru"]`.
+ src_vocab_file (`str`, *optional*):
+ File containing the vocabulary for the source language.
+ tgt_vocab_file (`st`, *optional*):
+ File containing the vocabulary for the target language.
+ merges_file (`str`, *optional*):
+ File containing the merges.
+ do_lower_case (`bool`, *optional*, defaults to `False`):
+ Whether or not to lowercase the input when tokenizing.
+ unk_token (`str`, *optional*, defaults to `""`):
+ The unknown token. A token that is not in the vocabulary cannot be converted to an ID and is set to be this
+ token instead.
+ bos_token (`str`, *optional*, defaults to `""`):
+ The beginning of sequence token that was used during pretraining. Can be used a sequence classifier token.
+
+
+
+ When building a sequence using special tokens, this is not the token that is used for the beginning of
+ sequence. The token used is the `cls_token`.
+
+
+
+ sep_token (`str`, *optional*, defaults to `""`):
+ The separator token, which is used when building a sequence from multiple sequences, e.g. two sequences for
+ sequence classification or for a text and a question for question answering. It is also used as the last
+ token of a sequence built with special tokens.
+ pad_token (`str`, *optional*, defaults to `""`):
+ The token used for padding, for example when batching sequences of different lengths.
+
+ """
+
+ vocab_files_names = VOCAB_FILES_NAMES
+ model_input_names = ["input_ids", "attention_mask"]
+
+ def __init__(
+ self,
+ langs=None,
+ src_vocab_file=None,
+ tgt_vocab_file=None,
+ merges_file=None,
+ do_lower_case=False,
+ unk_token="",
+ bos_token="",
+ sep_token="",
+ pad_token="",
+ **kwargs,
+ ):
+ try:
+ import sacremoses
+ except ImportError:
+ raise ImportError(
+ "You need to install sacremoses to use XLMTokenizer. "
+ "See https://pypi.org/project/sacremoses/ for installation."
+ )
+
+ self.sm = sacremoses
+
+ self.src_vocab_file = src_vocab_file
+ self.tgt_vocab_file = tgt_vocab_file
+ self.merges_file = merges_file
+ self.do_lower_case = do_lower_case
+
+ # cache of sm.MosesPunctNormalizer instance
+ self.cache_moses_punct_normalizer = {}
+ # cache of sm.MosesTokenizer instance
+ self.cache_moses_tokenizer = {}
+ self.cache_moses_detokenizer = {}
+
+ if langs and len(langs) == 2:
+ self.src_lang, self.tgt_lang = langs
+ else:
+ raise ValueError(
+ f"arg `langs` needs to be a list of 2 langs, e.g. ['en', 'ru'], but got {langs}. "
+ "Usually that means that tokenizer can't find a mapping for the given model path "
+ "in and other maps of this tokenizer."
+ )
+
+ with open(src_vocab_file, encoding="utf-8") as src_vocab_handle:
+ self.encoder = json.load(src_vocab_handle)
+ with open(tgt_vocab_file, encoding="utf-8") as tgt_vocab_handle:
+ tgt_vocab = json.load(tgt_vocab_handle)
+ self.decoder = {v: k for k, v in tgt_vocab.items()}
+ with open(merges_file, encoding="utf-8") as merges_handle:
+ merges = merges_handle.read().split("\n")[:-1]
+ merges = [tuple(merge.split()[:2]) for merge in merges]
+ self.bpe_ranks = dict(zip(merges, range(len(merges))))
+ self.cache = {}
+ super().__init__(
+ langs=langs,
+ src_vocab_file=src_vocab_file,
+ tgt_vocab_file=tgt_vocab_file,
+ merges_file=merges_file,
+ do_lower_case=do_lower_case,
+ unk_token=unk_token,
+ bos_token=bos_token,
+ sep_token=sep_token,
+ pad_token=pad_token,
+ **kwargs,
+ )
+
+ # hack override
+ def get_vocab(self) -> dict[str, int]:
+ return self.get_src_vocab()
+
+ # hack override
+ @property
+ def vocab_size(self) -> int:
+ return self.src_vocab_size
+
+ def moses_punct_norm(self, text, lang):
+ if lang not in self.cache_moses_punct_normalizer:
+ punct_normalizer = self.sm.MosesPunctNormalizer(lang=lang)
+ self.cache_moses_punct_normalizer[lang] = punct_normalizer
+ return self.cache_moses_punct_normalizer[lang].normalize(text)
+
+ def moses_tokenize(self, text, lang):
+ if lang not in self.cache_moses_tokenizer:
+ moses_tokenizer = self.sm.MosesTokenizer(lang=lang)
+ self.cache_moses_tokenizer[lang] = moses_tokenizer
+ return self.cache_moses_tokenizer[lang].tokenize(
+ text, aggressive_dash_splits=True, return_str=False, escape=True
+ )
+
+ def moses_detokenize(self, tokens, lang):
+ if lang not in self.cache_moses_detokenizer:
+ moses_detokenizer = self.sm.MosesDetokenizer(lang=lang)
+ self.cache_moses_detokenizer[lang] = moses_detokenizer
+ return self.cache_moses_detokenizer[lang].detokenize(tokens)
+
+ def moses_pipeline(self, text, lang):
+ text = replace_unicode_punct(text)
+ text = self.moses_punct_norm(text, lang)
+ text = remove_non_printing_char(text)
+ return text
+
+ @property
+ def src_vocab_size(self):
+ return len(self.encoder)
+
+ @property
+ def tgt_vocab_size(self):
+ return len(self.decoder)
+
+ def get_src_vocab(self):
+ return dict(self.encoder, **self.added_tokens_encoder)
+
+ def get_tgt_vocab(self):
+ return dict(self.decoder, **self.added_tokens_decoder)
+
+ def bpe(self, token):
+ word = tuple(token[:-1]) + (token[-1] + "",)
+ if token in self.cache:
+ return self.cache[token]
+ pairs = get_pairs(word)
+
+ if not pairs:
+ return token + ""
+
+ while True:
+ bigram = min(pairs, key=lambda pair: self.bpe_ranks.get(pair, float("inf")))
+ if bigram not in self.bpe_ranks:
+ break
+ first, second = bigram
+ new_word = []
+ i = 0
+ while i < len(word):
+ try:
+ j = word.index(first, i)
+ except ValueError:
+ new_word.extend(word[i:])
+ break
+ else:
+ new_word.extend(word[i:j])
+ i = j
+
+ if word[i] == first and i < len(word) - 1 and word[i + 1] == second:
+ new_word.append(first + second)
+ i += 2
+ else:
+ new_word.append(word[i])
+ i += 1
+ new_word = tuple(new_word)
+ word = new_word
+ if len(word) == 1:
+ break
+ else:
+ pairs = get_pairs(word)
+ word = " ".join(word)
+ if word == "\n ":
+ word = "\n"
+ self.cache[token] = word
+ return word
+
+ def _tokenize(self, text, lang="en", bypass_tokenizer=False):
+ """
+ Tokenize a string given language code using Moses.
+
+ Details of tokenization:
+
+ - [sacremoses](https://github.com/alvations/sacremoses): port of Moses
+ - Install with `pip install sacremoses`
+
+ Args:
+ - lang: ISO language code (default = 'en') (string). Languages should belong of the model supported
+ languages. However, we don't enforce it.
+ - bypass_tokenizer: Allow users to preprocess and tokenize the sentences externally (default = False)
+ (bool). If True, we only apply BPE.
+
+ Returns:
+ List of tokens.
+ """
+ # ignore `lang` which is currently isn't explicitly passed in tokenization_utils.py and always results in lang=en
+ # if lang != self.src_lang:
+ # raise ValueError(f"Expected lang={self.src_lang}, but got {lang}")
+ lang = self.src_lang
+
+ if self.do_lower_case:
+ text = text.lower()
+
+ if bypass_tokenizer:
+ text = text.split()
+ else:
+ text = self.moses_pipeline(text, lang=lang)
+ text = self.moses_tokenize(text, lang=lang)
+
+ split_tokens = []
+ for token in text:
+ if token:
+ split_tokens.extend(list(self.bpe(token).split(" ")))
+
+ return split_tokens
+
+ def _convert_token_to_id(self, token):
+ """Converts a token (str) in an id using the vocab."""
+ return self.encoder.get(token, self.encoder.get(self.unk_token))
+
+ def _convert_id_to_token(self, index):
+ """Converts an index (integer) in a token (str) using the vocab."""
+ return self.decoder.get(index, self.unk_token)
+
+ def convert_tokens_to_string(self, tokens):
+ """Converts a sequence of tokens (string) in a single string."""
+
+ # remove BPE
+ tokens = [t.replace(" ", "").replace("", " ") for t in tokens]
+ tokens = "".join(tokens).split()
+ # detokenize
+ text = self.moses_detokenize(tokens, self.tgt_lang)
+ return text
+
+ def build_inputs_with_special_tokens(
+ self, token_ids_0: list[int], token_ids_1: list[int] | None = None
+ ) -> list[int]:
+ """
+ Build model inputs from a sequence or a pair of sequence for sequence classification tasks by concatenating and
+ adding special tokens. A FAIRSEQ Transformer sequence has the following format:
+
+ - single sequence: ` X `
+ - pair of sequences: ` A B `
+
+ Args:
+ token_ids_0 (`List[int]`):
+ List of IDs to which the special tokens will be added.
+ token_ids_1 (`List[int]`, *optional*):
+ Optional second list of IDs for sequence pairs.
+
+ Returns:
+ `List[int]`: List of [input IDs](../glossary#input-ids) with the appropriate special tokens.
+ """
+ sep = [self.sep_token_id]
+
+ # no bos used in fairseq
+ if token_ids_1 is None:
+ return token_ids_0 + sep
+ return token_ids_0 + sep + token_ids_1 + sep
+
+ def get_special_tokens_mask(
+ self, token_ids_0: list[int], token_ids_1: list[int] | None = None, already_has_special_tokens: bool = False
+ ) -> list[int]:
+ """
+ Retrieve sequence ids from a token list that has no special tokens added. This method is called when adding
+ special tokens using the tokenizer `prepare_for_model` method.
+
+ Args:
+ token_ids_0 (`List[int]`):
+ List of IDs.
+ token_ids_1 (`List[int]`, *optional*):
+ Optional second list of IDs for sequence pairs.
+ already_has_special_tokens (`bool`, *optional*, defaults to `False`):
+ Whether or not the token list is already formatted with special tokens for the model.
+
+ Returns:
+ `List[int]`: A list of integers in the range [0, 1]: 1 for a special token, 0 for a sequence token.
+ """
+
+ if already_has_special_tokens:
+ return super().get_special_tokens_mask(
+ token_ids_0=token_ids_0, token_ids_1=token_ids_1, already_has_special_tokens=True
+ )
+ # no bos used in fairseq
+ if token_ids_1 is not None:
+ return ([0] * len(token_ids_0)) + [1] + ([0] * len(token_ids_1)) + [1]
+ return ([0] * len(token_ids_0)) + [1]
+
+ def save_vocabulary(self, save_directory: str, filename_prefix: str | None = None) -> tuple[str]:
+ if not os.path.isdir(save_directory):
+ logger.error(f"Vocabulary path ({save_directory}) should be a directory")
+ return
+
+ src_vocab_file = os.path.join(
+ save_directory, (filename_prefix + "-" if filename_prefix else "") + VOCAB_FILES_NAMES["src_vocab_file"]
+ )
+ tgt_vocab_file = os.path.join(
+ save_directory, (filename_prefix + "-" if filename_prefix else "") + VOCAB_FILES_NAMES["tgt_vocab_file"]
+ )
+ merges_file = os.path.join(
+ save_directory, (filename_prefix + "-" if filename_prefix else "") + VOCAB_FILES_NAMES["merges_file"]
+ )
+
+ with open(src_vocab_file, "w", encoding="utf-8") as f:
+ f.write(json.dumps(self.encoder, indent=2, sort_keys=True, ensure_ascii=False) + "\n")
+
+ with open(tgt_vocab_file, "w", encoding="utf-8") as f:
+ tgt_vocab = {v: k for k, v in self.decoder.items()}
+ f.write(json.dumps(tgt_vocab, indent=2, sort_keys=True, ensure_ascii=False) + "\n")
+
+ index = 0
+ with open(merges_file, "w", encoding="utf-8") as writer:
+ for bpe_tokens, token_index in sorted(self.bpe_ranks.items(), key=lambda kv: kv[1]):
+ if index != token_index:
+ logger.warning(
+ f"Saving vocabulary to {merges_file}: BPE merge indices are not consecutive."
+ " Please check that the tokenizer is not corrupted!"
+ )
+ index = token_index
+ writer.write(" ".join(bpe_tokens) + "\n")
+ index += 1
+
+ return src_vocab_file, tgt_vocab_file, merges_file
+
+ def __getstate__(self):
+ state = self.__dict__.copy()
+ state["sm"] = None
+ return state
+
+ def __setstate__(self, d):
+ self.__dict__ = d
+
+ try:
+ import sacremoses
+ except ImportError:
+ raise ImportError(
+ "You need to install sacremoses to use XLMTokenizer. "
+ "See https://pypi.org/project/sacremoses/ for installation."
+ )
+
+ self.sm = sacremoses
+
+
+__all__ = ["FSMTTokenizer"]
diff --git a/third_party/transformers/src/transformers/models/fuyu/__init__.py b/third_party/transformers/src/transformers/models/fuyu/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..c7fe71c84c576eaa70cab54a53ae5609061cee09
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/fuyu/__init__.py
@@ -0,0 +1,30 @@
+# Copyright 2024 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_fuyu import *
+ from .image_processing_fuyu import *
+ from .image_processing_pil_fuyu import *
+ from .modeling_fuyu import *
+ from .processing_fuyu import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/fuyu/configuration_fuyu.py b/third_party/transformers/src/transformers/models/fuyu/configuration_fuyu.py
new file mode 100644
index 0000000000000000000000000000000000000000..02de9af22306ab34688ef2b85d328654117331cd
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/fuyu/configuration_fuyu.py
@@ -0,0 +1,100 @@
+# Copyright 2023 Adept AI and the HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Fuyu model configuration"""
+
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...modeling_rope_utils import RopeParameters
+from ...utils import auto_docstring, logging
+from ..auto import CONFIG_MAPPING, AutoConfig
+
+
+logger = logging.get_logger(__name__)
+
+
+@auto_docstring(checkpoint="adept/fuyu-8b")
+@strict
+class FuyuConfig(PreTrainedConfig):
+ r"""
+ Example:
+
+ ```python
+ >>> from transformers import FuyuConfig
+
+ >>> # Initializing a Fuyu fuyu-7b style configuration
+ >>> configuration = FuyuConfig()
+ ```"""
+
+ model_type = "fuyu"
+ sub_configs = {"text_config": AutoConfig}
+ keys_to_ignore_at_inference = ["past_key_values"]
+ default_theta = 25000.0
+
+ vocab_size: int = 262144
+ hidden_size: int = 4096
+ intermediate_size: int = 16384
+ num_hidden_layers: int = 36
+ num_attention_heads: int = 64
+ hidden_act: str = "relu2"
+ max_position_embeddings: int = 16384
+ image_size: int | None = 300
+ patch_size: int | None = 30
+ num_channels: int | None = 3
+ initializer_range: float = 0.02
+ layer_norm_eps: float | None = 1e-5
+ use_cache: bool = True
+ tie_word_embeddings: bool = False
+ rope_parameters: RopeParameters | dict | None = None
+ qk_layernorm: bool | None = True
+ hidden_dropout: float | int | None = 0.0
+ attention_dropout: float | int | None = 0.0
+ pad_token_id: int | None = None
+ bos_token_id: int | None = 1
+ eos_token_id: int | list[int] | None = 2
+ image_token_id: int | None = 71011
+ text_config: dict | PreTrainedConfig | None = None
+
+ def __post_init__(self, **kwargs):
+ if self.text_config is None:
+ text_config = {
+ "vocab_size": self.vocab_size,
+ "max_position_embeddings": self.max_position_embeddings,
+ "hidden_size": self.hidden_size,
+ "intermediate_size": self.intermediate_size,
+ "num_hidden_layers": self.num_hidden_layers,
+ "num_attention_heads": self.num_attention_heads,
+ "hidden_act": self.hidden_act,
+ "initializer_range": self.initializer_range,
+ "layer_norm_eps": self.layer_norm_eps,
+ "use_cache": self.use_cache,
+ "rope_parameters": self.rope_parameters,
+ "qk_layernorm": self.qk_layernorm,
+ "hidden_dropout": self.hidden_dropout,
+ "attention_dropout": self.attention_dropout,
+ "pad_token_id": self.pad_token_id,
+ "bos_token_id": self.bos_token_id,
+ "eos_token_id": self.eos_token_id,
+ }
+ logger.info("text_config is None. initializing the text model with default values.")
+ self.text_config = CONFIG_MAPPING["persimmon"](**text_config)
+ elif isinstance(self.text_config, dict):
+ text_model_type = self.text_config.get("model_type", "persimmon")
+ self.text_config = CONFIG_MAPPING[text_model_type](**self.text_config)
+
+ kwargs.setdefault("partial_rotary_factor", 0.5) # assign default for BC
+ super().__post_init__(**kwargs)
+
+
+__all__ = ["FuyuConfig"]
diff --git a/third_party/transformers/src/transformers/models/fuyu/convert_fuyu_model_weights_to_hf.py b/third_party/transformers/src/transformers/models/fuyu/convert_fuyu_model_weights_to_hf.py
new file mode 100644
index 0000000000000000000000000000000000000000..919c016a0bb83fc339c31b2444e246744bb944bd
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/fuyu/convert_fuyu_model_weights_to_hf.py
@@ -0,0 +1,124 @@
+# Copyright 2023 The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import argparse
+import os
+import sys
+import warnings
+
+import flatdict
+import torch
+
+from transformers import FuyuConfig, FuyuForCausalLM, LlamaTokenizer
+
+
+try:
+ from transformers import LlamaTokenizerFast
+
+ tokenizer_class = LlamaTokenizerFast
+except ImportError as e:
+ warnings.warn(e)
+ warnings.warn(
+ "The converted tokenizer will be the `slow` tokenizer. To use the fast, update your `tokenizers` library and re-run the tokenizer conversion"
+ )
+ tokenizer_class = LlamaTokenizer
+
+"""
+
+If you have the original models, they can be loaded with:
+```py
+from transformers import FuyuForCausalLM, FuyuTokenizer
+
+model = FuyuForCausalLM.from_pretrained("/path/to/models/")
+tokenizer = FuyuTokenizer.from_pretrained("/path/to/models")
+```
+
+Important note: you need to be able to host the whole model in RAM to execute this script (even if the biggest versions
+come in several checkpoints they each contain a part of each weight of the model, so we need to load them all in RAM).
+"""
+
+
+KEYS_TO_MODIFY_MAPPING = {
+ "self_attention": "self_attn",
+ "language_model.encoder": "language_model.model",
+ "word_embeddings_for_head": "language_model.lm_head",
+ "language_model.embedding.word_embeddings": "language_model.model.embed_tokens",
+ "vit_encoder.linear_encoder": "vision_embed_tokens",
+}
+
+KEYS_TO_REMOVE = {
+ "rotary_emb.inv_freq",
+ "image_patch_projection",
+ "image_patch_projection.weight",
+ "image_patch_projection.bias",
+}
+
+
+def rename_state_dict(state_dict):
+ model_state_dict = {}
+ for key, value in state_dict.items():
+ for key_to_modify, new_key in KEYS_TO_MODIFY_MAPPING.items():
+ if key_to_modify in key:
+ key = key.replace(key_to_modify, new_key)
+ # if KEYS_TO_REMOVE in key:
+ if key in KEYS_TO_REMOVE:
+ continue
+ model_state_dict[key] = value
+ return model_state_dict
+
+
+def convert_fuyu_checkpoint(pytorch_dump_folder_path, ada_lib_path, pt_model_path):
+ sys.path.insert(0, ada_lib_path)
+ model_state_dict_base = torch.load(pt_model_path, map_location="cpu", weights_only=True)
+ state_dict = flatdict.FlatDict(model_state_dict_base["model"], ".")
+ state_dict = rename_state_dict(state_dict)
+
+ transformers_config = FuyuConfig()
+ model = FuyuForCausalLM(transformers_config).to(torch.bfloat16)
+ model.load_state_dict(state_dict)
+ model.save_pretrained(pytorch_dump_folder_path)
+ transformers_config.save_pretrained(pytorch_dump_folder_path)
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "--input_dir",
+ help="Location of Fuyu weights, which contains tokenizer.model and model folders",
+ )
+ parser.add_argument(
+ "--pt_model_path",
+ help="Location of Fuyu `model_optim_rng.pt`",
+ )
+ parser.add_argument(
+ "--output_dir",
+ help="Location to write HF model and tokenizer",
+ )
+ parser.add_argument(
+ "--ada_lib_path",
+ help="Location of original source code from adept to deserialize .pt checkpoint",
+ )
+ args = parser.parse_args()
+ spm_path = os.path.join(args.input_dir, "adept_vocab.model")
+
+ convert_fuyu_checkpoint(
+ pytorch_dump_folder_path=args.output_dir,
+ pt_model_path=args.pt_model_path,
+ ada_lib_path=args.ada_lib_path,
+ )
+ tokenizer = tokenizer_class(spm_path, bos_token="|ENDOFTEXT|", eos_token="|ENDOFTEXT|")
+ tokenizer.save_pretrained(args.output_dir)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/third_party/transformers/src/transformers/models/fuyu/image_processing_fuyu.py b/third_party/transformers/src/transformers/models/fuyu/image_processing_fuyu.py
new file mode 100644
index 0000000000000000000000000000000000000000..97d96ec923d7442089783dc0299019845be45951
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/fuyu/image_processing_fuyu.py
@@ -0,0 +1,530 @@
+# Copyright 2025 The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Image processor class for Fuyu."""
+
+import math
+
+import torch
+from torchvision.transforms.v2 import functional as tvF
+
+from ...image_processing_backends import TorchvisionBackend
+from ...image_processing_utils import BatchFeature, get_size_dict
+from ...image_transforms import group_images_by_shape, reorder_images
+from ...image_utils import (
+ ImageInput,
+ PILImageResampling,
+ SizeDict,
+ is_valid_image,
+ make_list_of_images,
+)
+from ...processing_utils import ImagesKwargs, Unpack
+from ...utils import (
+ TensorType,
+ auto_docstring,
+ logging,
+ requires_backends,
+)
+
+
+logger = logging.get_logger(__name__)
+
+
+def make_list_of_list_of_images(
+ images: list[list[ImageInput]] | list[ImageInput] | ImageInput,
+) -> list[list[ImageInput]]:
+ if is_valid_image(images):
+ return [[images]]
+
+ if isinstance(images, list) and all(isinstance(image, list) for image in images):
+ return images
+
+ if isinstance(images, list):
+ return [make_list_of_images(image) for image in images]
+
+ raise ValueError("images must be a list of list of images or a list of images or an image.")
+
+
+class FuyuImagesKwargs(ImagesKwargs, total=False):
+ r"""
+ patch_size (`dict[str, int]`, *optional*, defaults to `{"height": 30, "width": 30}`):
+ Dictionary in the format `{"height": int, "width": int}` specifying the size of the patches.
+ padding_value (`float`, *optional*, defaults to 1.0):
+ The value to pad the image with.
+ padding_mode (`str`, *optional*, defaults to "constant"):
+ The padding mode to use when padding the image.
+ """
+
+ patch_size: SizeDict | None
+ padding_value: float
+ padding_mode: str
+
+
+class FuyuBatchFeature(BatchFeature):
+ """
+ BatchFeature class for Fuyu image processor and processor.
+
+ The outputs dictionary from the processors contains a mix of tensors and lists of tensors.
+ """
+
+ def convert_to_tensors(self, tensor_type: str | TensorType | None = None, **kwargs):
+ """
+ Convert the inner content to tensors.
+
+ Args:
+ tensor_type (`str` or [`~utils.TensorType`], *optional*):
+ The type of tensors to use. If `str`, should be one of the values of the enum [`~utils.TensorType`]. If
+ `None`, no modification is done.
+ """
+ if tensor_type is None:
+ return self
+
+ is_tensor, as_tensor = self._get_is_as_tensor_fns(tensor_type=tensor_type)
+
+ def _convert_tensor(elem):
+ if is_tensor(elem):
+ return elem
+ return as_tensor(elem)
+
+ def _safe_convert_tensor(elem):
+ try:
+ return _convert_tensor(elem)
+ except: # noqa E722
+ if key == "overflowing_values":
+ raise ValueError("Unable to create tensor returning overflowing values of different lengths. ")
+ raise ValueError(
+ "Unable to create tensor, you should probably activate padding "
+ "with 'padding=True' to have batched tensors with the same length."
+ )
+
+ # Do the tensor conversion in batch
+ for key, value in self.items():
+ if isinstance(value, list) and isinstance(value[0], list):
+ # list[list[Any]] -> list[list[Tensor]]
+ self[key] = [[_safe_convert_tensor(elem) for elem in elems] for elems in value]
+ elif isinstance(value, list):
+ # list[Any] -> list[Tensor]
+ self[key] = [_safe_convert_tensor(elem) for elem in value]
+ else:
+ # Any -> Tensor
+ self[key] = _safe_convert_tensor(value)
+ return self
+
+ def to(self, *args, **kwargs) -> "BatchFeature":
+ """
+ Send all values to device by calling `v.to(*args, **kwargs)` (PyTorch only). This should support casting in
+ different `dtypes` and sending the `BatchFeature` to a different `device`.
+
+ Args:
+ args (`Tuple`):
+ Will be passed to the `to(...)` function of the tensors.
+ kwargs (`Dict`, *optional*):
+ Will be passed to the `to(...)` function of the tensors.
+
+ Returns:
+ [`BatchFeature`]: The same instance after modification.
+ """
+ requires_backends(self, ["torch"])
+ import torch
+
+ from ...utils import is_torch_device, is_torch_dtype
+
+ new_data = {}
+ device = kwargs.get("device")
+ # Check if the args are a device or a dtype
+ if device is None and len(args) > 0:
+ # device should be always the first argument
+ arg = args[0]
+ if is_torch_dtype(arg):
+ # The first argument is a dtype
+ pass
+ elif isinstance(arg, str) or is_torch_device(arg) or isinstance(arg, int):
+ device = arg
+ else:
+ # it's something else
+ raise ValueError(f"Attempting to cast a BatchFeature to type {str(arg)}. This is not supported.")
+
+ def _to(elem):
+ # check if v is a floating point
+ if torch.is_floating_point(elem):
+ # cast and send to device
+ return elem.to(*args, **kwargs)
+ if device is not None:
+ return elem.to(device=device)
+
+ return elem
+
+ # We cast only floating point tensors to avoid issues with tokenizers casting `LongTensor` to `FloatTensor`
+ for k, v in self.items():
+ if isinstance(v, list) and isinstance(v[0], list):
+ # Data structure is a list of lists
+ new_v = []
+ for elems in v:
+ new_v.append([_to(elem) for elem in elems])
+ new_data[k] = new_v
+ elif isinstance(v, list):
+ # Data structure is a list
+ new_data[k] = [_to(elem) for elem in v]
+ else:
+ new_data[k] = _to(v)
+ self.data = new_data
+ return self
+
+
+@auto_docstring
+class FuyuImageProcessor(TorchvisionBackend):
+ do_resize = True
+ size = {"height": 1080, "width": 1920}
+ patch_size = {"height": 30, "width": 30}
+ resample = PILImageResampling.BILINEAR
+ do_pad = True
+ padding_value = 1.0
+ padding_mode = "constant"
+ do_normalize = True
+ image_mean = 0.5
+ image_std = 0.5
+ do_rescale = True
+ rescale_factor = 1 / 255
+ model_input_names = [
+ "images",
+ "image_input_ids",
+ "image_patches",
+ "image_patch_indices_per_batch",
+ "image_patch_indices_per_subsequence",
+ ]
+ valid_kwargs = FuyuImagesKwargs
+
+ def __init__(self, **kwargs: Unpack[FuyuImagesKwargs]):
+ super().__init__(**kwargs)
+
+ def _prepare_images_structure(
+ self,
+ images: ImageInput,
+ expected_ndims: int = 3,
+ ) -> ImageInput:
+ images = self.fetch_images(images)
+ return make_list_of_list_of_images(images)
+
+ def resize(
+ self,
+ image: torch.Tensor,
+ size: SizeDict,
+ resample: "PILImageResampling | tvF.InterpolationMode | int | None" = None,
+ antialias: bool = True,
+ **kwargs,
+ ) -> torch.Tensor:
+ """
+ Resize an image to fit within `(size.height, size.width)` while maintaining aspect ratio.
+ Only resizes if the image is larger than the target size.
+ Args:
+ image (`torch.Tensor`):
+ Image to resize.
+ size (`SizeDict`):
+ Dictionary in the format `{"height": int, "width": int}` specifying the max size of the output image.
+ resample (`PILImageResampling | tvF.InterpolationMode | int`, *optional*, defaults to `PILImageResampling.BILINEAR`):
+ Resampling filter to use when resizing the image.
+ antialias (`bool`, *optional*, defaults to `True`):
+ Whether to apply antialiasing when resizing.
+ """
+ if resample is None:
+ resample = PILImageResampling.BILINEAR
+ image_height, image_width = image.shape[-2:]
+ target_height, target_width = size.height, size.width
+ # Only resize if image is larger than target
+ if image_width <= target_width and image_height <= target_height:
+ return image
+ # Calculate optimal scale factor to fit within target size
+ height_scale_factor = target_height / image_height
+ width_scale_factor = target_width / image_width
+ optimal_scale_factor = min(height_scale_factor, width_scale_factor)
+
+ new_height = int(image_height * optimal_scale_factor)
+ new_width = int(image_width * optimal_scale_factor)
+
+ return super().resize(
+ image, SizeDict(height=new_height, width=new_width), resample=resample, antialias=antialias
+ )
+
+ def _preprocess(
+ self,
+ images: list["torch.Tensor"],
+ do_resize: bool,
+ size: SizeDict,
+ resample: "PILImageResampling | tvF.InterpolationMode | int | None",
+ do_rescale: bool,
+ rescale_factor: float,
+ do_normalize: bool,
+ image_mean: float | list[float] | None,
+ image_std: float | list[float] | None,
+ do_pad: bool | None,
+ padding_value: float | None,
+ padding_mode: str | None,
+ disable_grouping: bool | None,
+ return_tensors: str | TensorType | None,
+ **kwargs,
+ ) -> FuyuBatchFeature:
+ # Group images by size for batched resizing
+ original_image_sizes = [batch_image[0].shape[-2:] for batch_image in images if batch_image]
+ grouped_images, grouped_images_index = group_images_by_shape(
+ images, disable_grouping=disable_grouping, is_nested=True
+ )
+ resized_images_grouped = {}
+ for shape, stacked_images in grouped_images.items():
+ if do_resize:
+ stacked_images = self.resize(image=stacked_images, size=size, resample=resample)
+ resized_images_grouped[shape] = stacked_images
+ resized_images = reorder_images(resized_images_grouped, grouped_images_index, is_nested=True)
+
+ image_sizes = [batch_image[0].shape[-2:] for batch_image in resized_images if batch_image]
+ image_unpadded_heights = [[image_size[0]] for image_size in image_sizes]
+ image_unpadded_widths = [[image_size[1]] for image_size in image_sizes]
+ image_scale_factors = [
+ [resized_size[0] / original_size[0]]
+ for original_size, resized_size in zip(original_image_sizes, image_sizes)
+ ]
+ if do_pad:
+ resized_images = self.pad(
+ resized_images,
+ pad_size=size,
+ fill_value=padding_value,
+ padding_mode=padding_mode,
+ disable_grouping=disable_grouping,
+ is_nested=True,
+ )
+ # Group images by size for further processing
+ # Needed in case do_resize is False, or resize returns images with different sizes
+ grouped_images, grouped_images_index = group_images_by_shape(
+ resized_images, disable_grouping=disable_grouping, is_nested=True
+ )
+ processed_images_grouped = {}
+ for shape, stacked_images in grouped_images.items():
+ # Fused rescale and normalize
+ stacked_images = self.rescale_and_normalize(
+ stacked_images, do_rescale, rescale_factor, do_normalize, image_mean, image_std
+ )
+ processed_images_grouped[shape] = stacked_images
+ processed_images = reorder_images(processed_images_grouped, grouped_images_index, is_nested=True)
+
+ return FuyuBatchFeature(
+ data={
+ "images": processed_images,
+ "image_unpadded_heights": image_unpadded_heights,
+ "image_unpadded_widths": image_unpadded_widths,
+ "image_scale_factors": image_scale_factors,
+ },
+ tensor_type=return_tensors,
+ )
+
+ def get_num_patches(self, image_height: int, image_width: int, patch_size: SizeDict | None = None) -> int:
+ """
+ Calculate number of patches required to encode an image.
+ Args:
+ image_height (`int`):
+ Height of the image.
+ image_width (`int`):
+ Width of the image.
+ patch_size (`SizeDict`, *optional*):
+ Dictionary in the format `{"height": int, "width": int}` specifying the size of the patches.
+ """
+ if patch_size is None:
+ if isinstance(self.patch_size, SizeDict):
+ patch_size = self.patch_size
+ else:
+ patch_size = SizeDict(**self.patch_size)
+ patch_height, patch_width = patch_size.height, patch_size.width
+ if image_height % patch_height != 0:
+ raise ValueError(f"{image_height=} must be divisible by {patch_height}")
+ if image_width % patch_width != 0:
+ raise ValueError(f"{image_width=} must be divisible by {patch_width}")
+ num_patches_per_dim_h = image_height // patch_height
+ num_patches_per_dim_w = image_width // patch_width
+ num_patches = num_patches_per_dim_h * num_patches_per_dim_w
+ return num_patches
+
+ def patchify_image(self, image: torch.Tensor, patch_size: SizeDict | None = None) -> torch.Tensor:
+ """
+ Convert an image into a tensor of patches using PyTorch's unfold operation.
+ Args:
+ image (`torch.Tensor`):
+ Image to convert. Shape: [batch, channels, height, width]
+ patch_size (`SizeDict`, *optional*):
+ Dictionary in the format `{"height": int, "width": int}` specifying the size of the patches.
+ """
+ requires_backends(self, ["torch"])
+ if patch_size is None:
+ if isinstance(self.patch_size, SizeDict):
+ patch_size = self.patch_size
+ else:
+ patch_size = SizeDict(**self.patch_size)
+ patch_height, patch_width = patch_size.height, patch_size.width
+ batch_size, channels, _, _ = image.shape
+ # Use unfold to extract patches
+ unfolded_along_height = image.unfold(2, patch_height, patch_height)
+ patches = unfolded_along_height.unfold(3, patch_width, patch_width)
+ patches = patches.contiguous()
+ # Reshape to [batch, num_patches, channels * patch_h * patch_w]
+ patches = patches.view(batch_size, channels, -1, patch_height, patch_width)
+ patches = patches.permute(0, 2, 3, 4, 1)
+ patches = patches.reshape(batch_size, -1, channels * patch_height * patch_width)
+ return patches
+
+ def preprocess_with_tokenizer_info(
+ self,
+ image_input: torch.Tensor,
+ image_present: torch.Tensor,
+ image_unpadded_h: torch.Tensor,
+ image_unpadded_w: torch.Tensor,
+ image_placeholder_id: int,
+ image_newline_id: int,
+ variable_sized: bool,
+ patch_size: dict[str, int] | None = None,
+ ) -> FuyuBatchFeature:
+ """
+ Process images for model input. In particular, variable-sized images are handled here.
+
+ Args:
+ image_input (`torch.Tensor` of shape [batch_size, subsequence_size, num_channels, height, width]):
+ Tensor of images padded to model input size.
+ image_present (`torch.Tensor` of shape [batch_size, subsequence_size, num_images]):
+ Tensor of 1s and 0s indicating whether an image is present.
+ image_unpadded_h (`torch.Tensor` of shape [batch_size, subsequence_size]):
+ Tensor of unpadded image heights.
+ image_unpadded_w (`torch.Tensor` of shape [batch_size, subsequence_size]):
+ Tensor of unpadded image widths.
+ image_placeholder_id (int):
+ The id of the image placeholder token. Comes from an associated tokenizer.
+ image_newline_id (int):
+ The id of the image newline token. Comes from an associated tokenizer.
+ variable_sized (bool):
+ Whether to process images as variable-sized.
+ patch_size (`dict[str, int]`, *optional*):
+ Size of the patches.
+ """
+ requires_backends(self, ["torch"])
+
+ if patch_size is None:
+ if isinstance(self.patch_size, SizeDict):
+ patch_size = self.patch_size
+ else:
+ patch_size = SizeDict(**self.patch_size)
+ elif not isinstance(patch_size, SizeDict):
+ patch_size = SizeDict(**patch_size)
+ patch_height, patch_width = patch_size.height, patch_size.width
+ # Only images that are present
+ images: list[list[torch.Tensor]] = []
+ batch_image_patches: list[list[torch.Tensor]] = []
+ # Image input ids for every subsequence, including ones with no image present
+ batch_image_input_ids: list[list[torch.Tensor]] = []
+ for batch_index in range(image_input.shape[0]):
+ image_input_ids = []
+ image_patches = []
+ for subseq_index in range(image_input.shape[1]):
+ if image_present[batch_index, subseq_index]:
+ image = image_input[batch_index, subseq_index]
+ image_height, image_width = image.shape[1], image.shape[2]
+ if variable_sized:
+ # Calculate new dimensions based on unpadded size
+ # The min() is required here due to floating point issues
+ new_h = min(
+ image_height,
+ math.ceil(image_unpadded_h[batch_index, subseq_index] / patch_height) * patch_height,
+ )
+ new_w = min(
+ image_width,
+ math.ceil(image_unpadded_w[batch_index, subseq_index] / patch_width) * patch_width,
+ )
+ image = image[:, :new_h, :new_w]
+ image_height, image_width = new_h, new_w
+ num_patches = self.get_num_patches(
+ image_height=image_height, image_width=image_width, patch_size=patch_size
+ )
+ # Create tensor of placeholder IDs
+ tensor_of_image_ids = torch.full(
+ [num_patches], image_placeholder_id, dtype=torch.int32, device=image_input.device
+ )
+ # Patchify the image
+ patches = self.patchify_image(image=image.unsqueeze(0), patch_size=patch_size).squeeze(0)
+ assert num_patches == patches.shape[0]
+ if variable_sized:
+ # Terminate each line with newline ID
+ tensor_of_image_ids = tensor_of_image_ids.reshape(-1, image_width // patch_width)
+ newline_ids = torch.full(
+ [tensor_of_image_ids.shape[0], 1],
+ image_newline_id,
+ dtype=torch.int32,
+ device=image_input.device,
+ )
+ tensor_of_image_ids = torch.cat([tensor_of_image_ids, newline_ids], dim=1)
+ tensor_of_image_ids = tensor_of_image_ids.reshape(-1)
+ images.append([image])
+ image_input_ids.append(tensor_of_image_ids)
+ image_patches.append(patches)
+ else:
+ image_input_ids.append(torch.tensor([], dtype=torch.int32, device=image_input.device))
+ batch_image_input_ids.append(image_input_ids)
+ batch_image_patches.append(image_patches)
+ # Create image patch indices
+ image_patch_indices_per_batch: list[list[torch.Tensor]] = []
+ image_patch_indices_per_subsequence: list[list[torch.Tensor]] = []
+
+ for sample_image_input_ids in batch_image_input_ids:
+ index_offset = 0
+ per_batch_indices = []
+ per_subsequence_indices = []
+ for subseq_image_input_ids in sample_image_input_ids:
+ # Indices of image patches
+ patches_mask = subseq_image_input_ids == image_placeholder_id
+ num_patches = torch.count_nonzero(patches_mask)
+ indices = torch.arange(num_patches, dtype=torch.int64, device=subseq_image_input_ids.device).type_as(
+ subseq_image_input_ids
+ )
+ # Place those indices in the image input ids token stream, with -1 representing non-index tokens
+ indices_in_stream_per_batch = torch.full_like(subseq_image_input_ids, -1)
+ indices_in_stream_per_subsequence = torch.full_like(subseq_image_input_ids, -1)
+ patches_inds = torch.nonzero(patches_mask, as_tuple=True)[0]
+
+ indices_in_stream_per_batch[patches_inds] = indices + index_offset
+ indices_in_stream_per_subsequence[patches_inds] = indices
+
+ per_batch_indices.append(indices_in_stream_per_batch)
+ per_subsequence_indices.append(indices_in_stream_per_subsequence)
+ index_offset += num_patches
+
+ image_patch_indices_per_batch.append(per_batch_indices)
+ image_patch_indices_per_subsequence.append(per_subsequence_indices)
+ return FuyuBatchFeature(
+ data={
+ "images": images,
+ "image_input_ids": batch_image_input_ids,
+ "image_patches": batch_image_patches,
+ "image_patch_indices_per_batch": image_patch_indices_per_batch,
+ "image_patch_indices_per_subsequence": image_patch_indices_per_subsequence,
+ }
+ )
+
+ def _standardize_kwargs(
+ self,
+ patch_size: dict[str, int] | SizeDict | None = None,
+ **kwargs,
+ ) -> dict:
+ """
+ Process Fuyu-specific kwargs before validation.
+ """
+ kwargs = super()._standardize_kwargs(**kwargs)
+ if patch_size is not None and not isinstance(patch_size, SizeDict):
+ patch_size = SizeDict(**get_size_dict(patch_size, param_name="patch_size"))
+ kwargs["patch_size"] = patch_size
+ return kwargs
+
+
+__all__ = ["FuyuImageProcessor"]
diff --git a/third_party/transformers/src/transformers/models/fuyu/image_processing_pil_fuyu.py b/third_party/transformers/src/transformers/models/fuyu/image_processing_pil_fuyu.py
new file mode 100644
index 0000000000000000000000000000000000000000..4e4787f4b7749313a0807df375b0a08be2eb3494
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/fuyu/image_processing_pil_fuyu.py
@@ -0,0 +1,580 @@
+# Copyright 2025 The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Image processor class for Fuyu."""
+
+import math
+from typing import TYPE_CHECKING
+
+import numpy as np
+
+from ...image_processing_backends import PilBackend
+from ...image_processing_utils import BatchFeature, get_size_dict
+from ...image_utils import (
+ ImageInput,
+ PILImageResampling,
+ SizeDict,
+ get_image_size,
+ is_valid_image,
+ make_list_of_images,
+)
+from ...processing_utils import ImagesKwargs, Unpack
+from ...utils import TensorType, auto_docstring, is_torch_available, requires_backends
+from ...utils.import_utils import requires
+
+
+if TYPE_CHECKING:
+ import torch
+
+if is_torch_available():
+ import torch
+
+
+# Adapted from transformers.models.fuyu.image_processing_fuyu.FuyuBatchFeature
+class FuyuBatchFeature(BatchFeature):
+ """
+ BatchFeature class for Fuyu image processor and processor.
+
+ The outputs dictionary from the processors contains a mix of tensors and lists of tensors.
+ """
+
+ def convert_to_tensors(self, tensor_type: str | TensorType | None = None, **kwargs):
+ """
+ Convert the inner content to tensors.
+
+ Args:
+ tensor_type (`str` or [`~utils.TensorType`], *optional*):
+ The type of tensors to use. If `str`, should be one of the values of the enum [`~utils.TensorType`]. If
+ `None`, no modification is done.
+ """
+ if tensor_type is None:
+ return self
+
+ is_tensor, as_tensor = self._get_is_as_tensor_fns(tensor_type=tensor_type)
+
+ def _convert_tensor(elem):
+ if is_tensor(elem):
+ return elem
+ return as_tensor(elem)
+
+ def _safe_convert_tensor(elem):
+ try:
+ return _convert_tensor(elem)
+ except: # noqa E722
+ if key == "overflowing_values":
+ raise ValueError("Unable to create tensor returning overflowing values of different lengths. ")
+ raise ValueError(
+ "Unable to create tensor, you should probably activate padding "
+ "with 'padding=True' to have batched tensors with the same length."
+ )
+
+ # Do the tensor conversion in batch
+ for key, value in self.items():
+ if isinstance(value, list) and isinstance(value[0], list):
+ # list[list[Any]] -> list[list[Tensor]]
+ self[key] = [[_safe_convert_tensor(elem) for elem in elems] for elems in value]
+ elif isinstance(value, list):
+ # list[Any] -> list[Tensor]
+ self[key] = [_safe_convert_tensor(elem) for elem in value]
+ else:
+ # Any -> Tensor
+ self[key] = _safe_convert_tensor(value)
+ return self
+
+ def to(self, *args, **kwargs) -> "BatchFeature":
+ """
+ Send all values to device by calling `v.to(*args, **kwargs)` (PyTorch only). This should support casting in
+ different `dtypes` and sending the `BatchFeature` to a different `device`.
+
+ Args:
+ args (`Tuple`):
+ Will be passed to the `to(...)` function of the tensors.
+ kwargs (`Dict`, *optional*):
+ Will be passed to the `to(...)` function of the tensors.
+
+ Returns:
+ [`BatchFeature`]: The same instance after modification.
+ """
+ requires_backends(self, ["torch"])
+ import torch
+
+ from ...utils import is_torch_device, is_torch_dtype
+
+ new_data = {}
+ device = kwargs.get("device")
+ # Check if the args are a device or a dtype
+ if device is None and len(args) > 0:
+ # device should be always the first argument
+ arg = args[0]
+ if is_torch_dtype(arg):
+ # The first argument is a dtype
+ pass
+ elif isinstance(arg, str) or is_torch_device(arg) or isinstance(arg, int):
+ device = arg
+ else:
+ # it's something else
+ raise ValueError(f"Attempting to cast a BatchFeature to type {str(arg)}. This is not supported.")
+
+ def _to(elem):
+ # check if v is a floating point
+ if torch.is_floating_point(elem):
+ # cast and send to device
+ return elem.to(*args, **kwargs)
+ if device is not None:
+ return elem.to(device=device)
+
+ return elem
+
+ # We cast only floating point tensors to avoid issues with tokenizers casting `LongTensor` to `FloatTensor`
+ for k, v in self.items():
+ if isinstance(v, list) and isinstance(v[0], list):
+ # Data structure is a list of lists
+ new_v = []
+ for elems in v:
+ new_v.append([_to(elem) for elem in elems])
+ new_data[k] = new_v
+ elif isinstance(v, list):
+ # Data structure is a list
+ new_data[k] = [_to(elem) for elem in v]
+ else:
+ new_data[k] = _to(v)
+ self.data = new_data
+ return self
+
+
+# Adapted from transformers.models.fuyu.image_processing_fuyu.FuyuImagesKwargs
+class FuyuImagesKwargs(ImagesKwargs, total=False):
+ r"""
+ patch_size (`dict[str, int]`, *optional*, defaults to `{"height": 30, "width": 30}`):
+ Dictionary in the format `{"height": int, "width": int}` specifying the size of the patches.
+ padding_value (`float`, *optional*, defaults to 1.0):
+ The value to pad the image with.
+ padding_mode (`str`, *optional*, defaults to "constant"):
+ The padding mode to use when padding the image.
+ """
+
+ patch_size: SizeDict | None
+ padding_value: float
+ padding_mode: str
+
+
+# Adapted from transformers.models.fuyu.image_processing_fuyu.make_list_of_list_of_images
+def make_list_of_list_of_images(
+ images: list[list[ImageInput]] | list[ImageInput] | ImageInput,
+) -> list[list[ImageInput]]:
+ if is_valid_image(images):
+ return [[images]]
+
+ if isinstance(images, list) and all(isinstance(image, list) for image in images):
+ return images
+
+ if isinstance(images, list):
+ return [make_list_of_images(image) for image in images]
+
+ raise ValueError("images must be a list of list of images or a list of images or an image.")
+
+
+@auto_docstring
+@requires(backends=("torch",))
+class FuyuImageProcessorPil(PilBackend):
+ do_resize = True
+ size = {"height": 1080, "width": 1920}
+ patch_size = {"height": 30, "width": 30}
+ resample = PILImageResampling.BILINEAR
+ do_pad = True
+ padding_value = 1.0
+ padding_mode = "constant"
+ do_normalize = True
+ image_mean = 0.5
+ image_std = 0.5
+ do_rescale = True
+ rescale_factor = 1 / 255
+ model_input_names = [
+ "images",
+ "image_input_ids",
+ "image_patches",
+ "image_patch_indices_per_batch",
+ "image_patch_indices_per_subsequence",
+ ]
+ valid_kwargs = FuyuImagesKwargs
+
+ def __init__(self, **kwargs: Unpack[FuyuImagesKwargs]):
+ super().__init__(**kwargs)
+
+ def _prepare_images_structure(self, images: ImageInput, expected_ndims: int = 3) -> ImageInput:
+ images = self.fetch_images(images)
+ return make_list_of_list_of_images(images)
+
+ def resize(
+ self,
+ image: np.ndarray,
+ size: SizeDict,
+ resample: PILImageResampling | None = None,
+ **kwargs,
+ ) -> np.ndarray:
+ """
+ Resize an image to fit within `(size.height, size.width)` while maintaining aspect ratio.
+ Only resizes if the image is larger than the target size.
+ Args:
+ image (`np.ndarray`):
+ Image to resize.
+ size (`SizeDict`):
+ Dictionary in the format `{"height": int, "width": int}` specifying the max size of the output image.
+ resample (`PILImageResampling | tvF.InterpolationMode | int`, *optional*, defaults to `PILImageResampling.BILINEAR`):
+ Resampling filter to use when resizing the image.
+ """
+
+ input_height, input_width = image.shape[-2:]
+ target_height, target_width = size.height, size.width
+ # Only resize if image is larger than target
+ if input_width <= target_width and input_height <= target_height:
+ return image
+ # Calculate optimal scale factor to fit within target size
+ height_scale_factor = target_height / input_height
+ width_scale_factor = target_width / input_width
+ optimal_scale_factor = min(height_scale_factor, width_scale_factor)
+
+ new_height = int(input_height * optimal_scale_factor)
+ new_width = int(input_width * optimal_scale_factor)
+
+ return super().resize(image, SizeDict(height=new_height, width=new_width), resample=resample)
+
+ def _preprocess(
+ self,
+ images: list[list[np.ndarray]],
+ do_resize: bool,
+ size: SizeDict,
+ resample: PILImageResampling | None,
+ do_rescale: bool,
+ rescale_factor: float,
+ do_normalize: bool,
+ image_mean: float | list[float] | None,
+ image_std: float | list[float] | None,
+ do_pad: bool | None,
+ padding_value: float | None,
+ padding_mode: str | None,
+ return_tensors: str | TensorType | None,
+ **kwargs,
+ ) -> FuyuBatchFeature:
+ # Process nested images one by one
+ original_image_sizes = []
+ processed_images = []
+ for batch_images in images:
+ if batch_images:
+ original_image_sizes.append(batch_images[0].shape[-2:])
+ processed_batch = []
+ for image in batch_images:
+ if do_resize:
+ image = self.resize(image=image, size=size, resample=resample)
+ processed_batch.append(image)
+ processed_images.append(processed_batch)
+ else:
+ processed_images.append([])
+
+ image_sizes = [batch_image[0].shape[-2:] for batch_image in processed_images if batch_image]
+ image_unpadded_heights = [[image_size[0]] for image_size in image_sizes]
+ image_unpadded_widths = [[image_size[1]] for image_size in image_sizes]
+ image_scale_factors = [
+ [resized_size[0] / original_size[0]]
+ for original_size, resized_size in zip(original_image_sizes, image_sizes)
+ ]
+
+ if do_pad:
+ # Handle nested padding manually since PIL backend doesn't support is_nested
+ target_height, target_width = size.height, size.width
+ for batch_idx, batch_images in enumerate(processed_images):
+ for img_idx, image in enumerate(batch_images):
+ from ...image_utils import ChannelDimension
+
+ height, width = get_image_size(image, channel_dim=ChannelDimension.FIRST)
+ padding_height = target_height - height
+ padding_width = target_width - width
+ if padding_height > 0 or padding_width > 0:
+ pad_width = ((0, 0), (0, padding_height), (0, padding_width))
+ if padding_mode == "constant":
+ image = np.pad(image, pad_width, mode="constant", constant_values=padding_value)
+ else:
+ image = np.pad(image, pad_width, mode=padding_mode)
+ processed_images[batch_idx][img_idx] = image
+
+ # Process rescale and normalize one by one
+ for batch_idx, batch_images in enumerate(processed_images):
+ for img_idx, image in enumerate(batch_images):
+ if do_rescale:
+ image = self.rescale(image, rescale_factor)
+ if do_normalize:
+ image = self.normalize(image, image_mean, image_std)
+ processed_images[batch_idx][img_idx] = image
+
+ return FuyuBatchFeature(
+ data={
+ "images": processed_images,
+ "image_unpadded_heights": image_unpadded_heights,
+ "image_unpadded_widths": image_unpadded_widths,
+ "image_scale_factors": image_scale_factors,
+ },
+ tensor_type=return_tensors,
+ )
+
+ def get_num_patches(self, image_height: int, image_width: int, patch_size: SizeDict | None = None) -> int:
+ """
+ Calculate number of patches required to encode an image.
+ Args:
+ image_height (`int`):
+ Height of the image.
+ image_width (`int`):
+ Width of the image.
+ patch_size (`SizeDict`, *optional*):
+ Dictionary in the format `{"height": int, "width": int}` specifying the size of the patches.
+ """
+ if patch_size is None:
+ if isinstance(self.patch_size, SizeDict):
+ patch_size = self.patch_size
+ else:
+ patch_size = SizeDict(**self.patch_size)
+ patch_height, patch_width = patch_size.height, patch_size.width
+ if image_height % patch_height != 0:
+ raise ValueError(f"{image_height=} must be divisible by {patch_height}")
+ if image_width % patch_width != 0:
+ raise ValueError(f"{image_width=} must be divisible by {patch_width}")
+ num_patches_per_dim_h = image_height // patch_height
+ num_patches_per_dim_w = image_width // patch_width
+ num_patches = num_patches_per_dim_h * num_patches_per_dim_w
+ return num_patches
+
+ def patchify_image(
+ self, image: "np.ndarray | torch.Tensor", patch_size: SizeDict | None = None
+ ) -> "np.ndarray | torch.Tensor":
+ """
+ Convert an image into a tensor of patches using numpy operations.
+ Args:
+ image (`np.ndarray` or `torch.Tensor`):
+ Image to convert. Shape: [batch, channels, height, width] or [channels, height, width]
+ patch_size (`SizeDict`, *optional*):
+ Dictionary in the format `{"height": int, "width": int}` specifying the size of the patches.
+ """
+ requires_backends(self, ["torch"])
+ import torch
+
+ if patch_size is None:
+ if isinstance(self.patch_size, SizeDict):
+ patch_size = self.patch_size
+ else:
+ patch_size = SizeDict(**self.patch_size)
+ patch_height, patch_width = patch_size.height, patch_size.width
+
+ # Handle torch tensors by converting to numpy
+ is_torch = isinstance(image, torch.Tensor)
+ if is_torch:
+ image_np = image.cpu().numpy()
+ device = image.device
+ else:
+ image_np = image
+ device = None
+
+ # Handle batch dimension
+ if len(image_np.shape) == 4:
+ batch_size, channels, height, width = image_np.shape
+ elif len(image_np.shape) == 3:
+ batch_size = 1
+ channels, height, width = image_np.shape
+ image_np = image_np[np.newaxis, ...]
+ else:
+ raise ValueError(
+ f"Expected image shape [batch, channels, height, width] or [channels, height, width], got {image_np.shape}"
+ )
+
+ # Extract patches using numpy operations to match torch unfold behavior exactly
+ # Torch: unfold(2) -> unfold(3) -> view(b, c, -1, h, w) -> permute(0,2,3,4,1) -> reshape(b, -1, c*h*w)
+ num_patches_h = height // patch_height
+ num_patches_w = width // patch_width
+ num_patches = num_patches_h * num_patches_w
+
+ patches_list = []
+ for b in range(batch_size):
+ # Simulate torch unfold: extract patches along height, then width
+ # After unfold(2) and unfold(3), shape is (channels, num_patches_h, patch_height, num_patches_w, patch_width)
+ # After view: (channels, num_patches, patch_height, patch_width) where num_patches = num_patches_h * num_patches_w
+ # After permute(0,2,3,4,1): (num_patches, patch_height, patch_width, channels)
+ # After reshape: (num_patches, channels * patch_height * patch_width)
+
+ # Reshape to extract patches: (channels, num_patches_h, patch_height, num_patches_w, patch_width)
+ img_reshaped = image_np[b].reshape(channels, num_patches_h, patch_height, num_patches_w, patch_width)
+ # Transpose to (channels, num_patches, patch_height, patch_width) where num_patches = num_patches_h * num_patches_w
+ img_reshaped = img_reshaped.transpose(0, 1, 3, 2, 4).reshape(
+ channels, num_patches, patch_height, patch_width
+ )
+ # Permute to (num_patches, patch_height, patch_width, channels) - matching torch permute(0,2,3,4,1)
+ img_permuted = img_reshaped.transpose(1, 2, 3, 0)
+ # Flatten to (num_patches, channels * patch_height * patch_width)
+ patches = img_permuted.reshape(num_patches, channels * patch_height * patch_width)
+ patches_list.append(patches)
+
+ patches_array = np.stack(patches_list, axis=0) if batch_size > 1 else patches_list[0]
+
+ # Convert back to torch if input was torch
+ if is_torch:
+ patches_array = torch.from_numpy(patches_array).to(device)
+
+ return patches_array
+
+ def preprocess_with_tokenizer_info(
+ self,
+ image_input: "torch.Tensor",
+ image_present: "torch.Tensor",
+ image_unpadded_h: "torch.Tensor",
+ image_unpadded_w: "torch.Tensor",
+ image_placeholder_id: int,
+ image_newline_id: int,
+ variable_sized: bool,
+ patch_size: dict[str, int] | None = None,
+ ) -> FuyuBatchFeature:
+ """
+ Process images for model input. In particular, variable-sized images are handled here.
+ This method uses PyTorch operations as it operates on model inputs which are tensors.
+
+ Args:
+ image_input (`torch.Tensor` of shape [batch_size, subsequence_size, num_channels, height, width]):
+ Tensor of images padded to model input size.
+ image_present (`torch.Tensor` of shape [batch_size, subsequence_size, num_images]):
+ Tensor of 1s and 0s indicating whether an image is present.
+ image_unpadded_h (`torch.Tensor` of shape [batch_size, subsequence_size]):
+ Tensor of unpadded image heights.
+ image_unpadded_w (`torch.Tensor` of shape [batch_size, subsequence_size]):
+ Tensor of unpadded image widths.
+ image_placeholder_id (int):
+ The id of the image placeholder token. Comes from an associated tokenizer.
+ image_newline_id (int):
+ The id of the image newline token. Comes from an associated tokenizer.
+ variable_sized (bool):
+ Whether to process images as variable-sized.
+ patch_size (`dict[str, int]`, *optional*):
+ Size of the patches.
+ """
+ requires_backends(self, ["torch"])
+ import torch
+
+ if patch_size is None:
+ if isinstance(self.patch_size, SizeDict):
+ patch_size = self.patch_size
+ else:
+ patch_size = SizeDict(**self.patch_size)
+ elif not isinstance(patch_size, SizeDict):
+ patch_size = SizeDict(**patch_size)
+ patch_height, patch_width = patch_size.height, patch_size.width
+ # Only images that are present
+ images: list[list[torch.Tensor]] = []
+ batch_image_patches: list[list[torch.Tensor]] = []
+ # Image input ids for every subsequence, including ones with no image present
+ batch_image_input_ids: list[list[torch.Tensor]] = []
+ for batch_index in range(image_input.shape[0]):
+ image_input_ids = []
+ image_patches = []
+ for subseq_index in range(image_input.shape[1]):
+ if image_present[batch_index, subseq_index]:
+ image = image_input[batch_index, subseq_index]
+ image_height, image_width = image.shape[1], image.shape[2]
+ if variable_sized:
+ # Calculate new dimensions based on unpadded size
+ # The min() is required here due to floating point issues
+ new_h = min(
+ image_height,
+ math.ceil(image_unpadded_h[batch_index, subseq_index] / patch_height) * patch_height,
+ )
+ new_w = min(
+ image_width,
+ math.ceil(image_unpadded_w[batch_index, subseq_index] / patch_width) * patch_width,
+ )
+ image = image[:, :new_h, :new_w]
+ image_height, image_width = new_h, new_w
+ num_patches = self.get_num_patches(
+ image_height=image_height, image_width=image_width, patch_size=patch_size
+ )
+ # Create tensor of placeholder IDs
+ tensor_of_image_ids = torch.full(
+ [num_patches], image_placeholder_id, dtype=torch.int32, device=image_input.device
+ )
+ # Patchify the image - convert to numpy, patchify, convert back
+ image_np = image.cpu().numpy()
+ patches_np = self.patchify_image(image_np, patch_size=patch_size)
+ patches = torch.from_numpy(patches_np).to(image_input.device)
+ assert num_patches == patches.shape[0]
+ if variable_sized:
+ # Terminate each line with newline ID
+ tensor_of_image_ids = tensor_of_image_ids.reshape(-1, image_width // patch_width)
+ newline_ids = torch.full(
+ [tensor_of_image_ids.shape[0], 1],
+ image_newline_id,
+ dtype=torch.int32,
+ device=image_input.device,
+ )
+ tensor_of_image_ids = torch.cat([tensor_of_image_ids, newline_ids], dim=1)
+ tensor_of_image_ids = tensor_of_image_ids.reshape(-1)
+ images.append([image])
+ image_input_ids.append(tensor_of_image_ids)
+ image_patches.append(patches)
+ else:
+ image_input_ids.append(torch.tensor([], dtype=torch.int32, device=image_input.device))
+ batch_image_input_ids.append(image_input_ids)
+ batch_image_patches.append(image_patches)
+ # Create image patch indices
+ image_patch_indices_per_batch: list[list[torch.Tensor]] = []
+ image_patch_indices_per_subsequence: list[list[torch.Tensor]] = []
+
+ for sample_image_input_ids in batch_image_input_ids:
+ index_offset = 0
+ per_batch_indices = []
+ per_subsequence_indices = []
+ for subseq_image_input_ids in sample_image_input_ids:
+ # Indices of image patches
+ patches_mask = subseq_image_input_ids == image_placeholder_id
+ num_patches = torch.count_nonzero(patches_mask)
+ indices = torch.arange(num_patches, dtype=torch.int64, device=subseq_image_input_ids.device).type_as(
+ subseq_image_input_ids
+ )
+ # Place those indices in the image input ids token stream, with -1 representing non-index tokens
+ indices_in_stream_per_batch = torch.full_like(subseq_image_input_ids, -1)
+ indices_in_stream_per_subsequence = torch.full_like(subseq_image_input_ids, -1)
+ patches_inds = torch.nonzero(patches_mask, as_tuple=True)[0]
+
+ indices_in_stream_per_batch[patches_inds] = indices + index_offset
+ indices_in_stream_per_subsequence[patches_inds] = indices
+
+ per_batch_indices.append(indices_in_stream_per_batch)
+ per_subsequence_indices.append(indices_in_stream_per_subsequence)
+ index_offset += num_patches
+
+ image_patch_indices_per_batch.append(per_batch_indices)
+ image_patch_indices_per_subsequence.append(per_subsequence_indices)
+ return FuyuBatchFeature(
+ data={
+ "images": images,
+ "image_input_ids": batch_image_input_ids,
+ "image_patches": batch_image_patches,
+ "image_patch_indices_per_batch": image_patch_indices_per_batch,
+ "image_patch_indices_per_subsequence": image_patch_indices_per_subsequence,
+ }
+ )
+
+ def _standardize_kwargs(self, patch_size: dict[str, int] | SizeDict | None = None, **kwargs) -> dict:
+ """
+ Process Fuyu-specific kwargs before validation.
+ """
+ kwargs = super()._standardize_kwargs(**kwargs)
+ if patch_size is not None and not isinstance(patch_size, SizeDict):
+ patch_size = SizeDict(**get_size_dict(patch_size, param_name="patch_size"))
+ kwargs["patch_size"] = patch_size
+ return kwargs
+
+
+__all__ = ["FuyuImageProcessorPil"]
diff --git a/third_party/transformers/src/transformers/models/fuyu/modeling_fuyu.py b/third_party/transformers/src/transformers/models/fuyu/modeling_fuyu.py
new file mode 100644
index 0000000000000000000000000000000000000000..df57519032b959cf9d3c15c5572c401b8ac2b4ae
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/fuyu/modeling_fuyu.py
@@ -0,0 +1,345 @@
+# Copyright 2023 HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""PyTorch Fuyu model."""
+
+import torch
+from torch import nn
+
+from ...cache_utils import Cache
+from ...generation import GenerationMixin
+from ...modeling_outputs import BaseModelOutputWithPooling, CausalLMOutputWithPast
+from ...modeling_utils import PreTrainedModel
+from ...models.auto.modeling_auto import AutoModel
+from ...processing_utils import Unpack
+from ...utils import TransformersKwargs, auto_docstring, can_return_tuple, logging, torch_compilable_check
+from .configuration_fuyu import FuyuConfig
+
+
+logger = logging.get_logger(__name__)
+
+
+@auto_docstring
+class FuyuPreTrainedModel(PreTrainedModel):
+ config: FuyuConfig
+ base_model_prefix = "model"
+ input_modalities = ("image", "text")
+ supports_gradient_checkpointing = True
+ _supports_attention_backend = True
+ _supports_flash_attn = True
+ _supports_sdpa = True
+ _supports_flex_attn = True
+ _no_split_modules = []
+ _skip_keys_device_placement = "past_key_values"
+
+
+@auto_docstring(
+ custom_intro="""
+ The Fuyu model which consists of a vision backbone and a language model, without a language modeling head.
+ """
+)
+class FuyuModel(FuyuPreTrainedModel):
+ def __init__(self, config: FuyuConfig):
+ super().__init__(config)
+ self.padding_idx = config.pad_token_id
+ self.vocab_size = config.text_config.vocab_size
+ self.language_model = AutoModel.from_config(config.text_config)
+ self.vision_embed_tokens = nn.Linear(
+ config.patch_size * config.patch_size * config.num_channels, config.hidden_size
+ )
+
+ self.gradient_checkpointing = False
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ def get_input_embeddings(self):
+ return self.language_model.get_input_embeddings()
+
+ def set_input_embeddings(self, value):
+ self.language_model.set_input_embeddings(value)
+
+ def gather_continuous_embeddings(
+ self,
+ word_embeddings: torch.Tensor,
+ continuous_embeddings: list[torch.Tensor],
+ image_patch_input_indices: torch.Tensor,
+ ) -> torch.Tensor:
+ """This function places the continuous_embeddings into the word_embeddings at the locations
+ indicated by image_patch_input_indices. Different batch elements can have different numbers of continuous
+ embeddings.
+
+ Args:
+ word_embeddings (`torch.FloatTensor` of shape `(batch_size, sequence_length, hidden_size)`):
+ Tensor of word embeddings.
+ continuous_embeddings (`torch.FloatTensor` of shape `(batch_size, num_patches, hidden_size)`):
+ Tensor of continuous embeddings. The length of the list is the batch size. Each entry is shape
+ [num_image_embeddings, hidden], and num_image_embeddings needs to match the number of non-negative
+ indices in image_patch_input_indices for that batch element.
+ image_patch_input_indices (`torch.LongTensor` of shape `(batch_size, sequence_length)`):
+ Tensor of indices of the image patches in the input_ids tensor.
+ """
+ if not (word_embeddings.shape[0] == len(continuous_embeddings)):
+ raise ValueError(
+ f"Batch sizes must match! Got {len(continuous_embeddings)=} and {word_embeddings.shape[0]=}"
+ )
+
+ output_embeddings = word_embeddings.clone()
+ for batch_idx in range(word_embeddings.shape[0]):
+ # First, find the positions of all the non-negative values in image_patch_input_indices, those are the
+ # positions in word_embeddings that we want to replace with content from continuous_embeddings.
+ dst_indices = torch.nonzero(image_patch_input_indices[batch_idx] >= 0, as_tuple=True)[0]
+ # Next look up those indices in image_patch_input_indices to find the indices in continuous_embeddings that we
+ # want to use to replace the values in word_embeddings.
+ src_indices = image_patch_input_indices[batch_idx][dst_indices]
+ # Check if we have more indices than embeddings. Note that we could have fewer indices if images got truncated.
+ if src_indices.shape[0] > continuous_embeddings[batch_idx].shape[0]:
+ raise ValueError(
+ f"Number of continuous embeddings {continuous_embeddings[batch_idx].shape=} does not match "
+ f"number of continuous token ids {src_indices.shape=} in batch element {batch_idx}."
+ )
+ output_embeddings[batch_idx, dst_indices] = continuous_embeddings[batch_idx][src_indices].to(
+ output_embeddings.device
+ )
+ return output_embeddings
+
+ @can_return_tuple
+ @auto_docstring
+ def get_image_features(
+ self, pixel_values: torch.FloatTensor, **kwargs: Unpack[TransformersKwargs]
+ ) -> tuple | BaseModelOutputWithPooling:
+ r"""
+ pixel_values (`torch.FloatTensor` of shape `(batch_size, num_channels, image_size, image_size)`):
+ The tensors corresponding to the input images.
+ """
+ patch_embeddings = self.vision_embed_tokens(pixel_values)
+ return BaseModelOutputWithPooling(last_hidden_state=patch_embeddings)
+
+ def get_placeholder_mask(
+ self, input_ids: torch.LongTensor, inputs_embeds: torch.FloatTensor, image_features: torch.FloatTensor
+ ):
+ """
+ Obtains multimodal placeholder mask from `input_ids` or `inputs_embeds`, and checks that the placeholder token count is
+ equal to the length of multimodal features. If the lengths are different, an error is raised.
+ """
+ if input_ids is None:
+ special_image_mask = inputs_embeds == self.get_input_embeddings()(
+ torch.tensor(self.config.image_token_id, dtype=torch.long, device=inputs_embeds.device)
+ )
+ special_image_mask = special_image_mask.all(-1)
+ else:
+ special_image_mask = input_ids == self.config.image_token_id
+
+ n_image_tokens = special_image_mask.sum()
+ n_image_features = image_features.shape[0] * image_features.shape[1]
+ special_image_mask = special_image_mask.unsqueeze(-1).expand_as(inputs_embeds).to(inputs_embeds.device)
+ torch_compilable_check(
+ inputs_embeds[special_image_mask].numel() == image_features.numel(),
+ f"Image features and image tokens do not match, tokens: {n_image_tokens}, features: {n_image_features}",
+ )
+ return special_image_mask
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ # [batch_size, num_total_patches, patch_size_ x patch_size x num_channels ]
+ image_patches: torch.Tensor | None = None,
+ image_patches_indices: torch.Tensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ use_cache: bool | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | CausalLMOutputWithPast:
+ r"""
+ image_patches (`torch.FloatTensor` of shape `(batch_size, num_total_patches, patch_size_ x patch_size x num_channels)`, *optional*):
+ Image patches to be used as continuous embeddings. The patches are flattened and then projected to the
+ hidden size of the model.
+ image_patches_indices (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*):
+ Tensor of indices of the image patches in the input_ids tensor.
+ """
+ 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 None:
+ inputs_embeds = self.language_model.get_input_embeddings()(input_ids)
+
+ seq_len = inputs_embeds.shape[1]
+
+ if position_ids is None:
+ device = input_ids.device if input_ids is not None else inputs_embeds.device
+ past_key_values_length = past_key_values.get_seq_length() if past_key_values is not None else 0
+ position_ids = torch.arange(
+ past_key_values_length, seq_len + past_key_values_length, dtype=torch.long, device=device
+ )
+ position_ids = position_ids.unsqueeze(0)
+
+ if image_patches is not None:
+ patch_embeddings = self.get_image_features(image_patches, return_dict=True).last_hidden_state
+ patch_embeddings = patch_embeddings.to(inputs_embeds.device, inputs_embeds.dtype)
+ special_image_mask = self.get_placeholder_mask(
+ input_ids, inputs_embeds=inputs_embeds, image_features=patch_embeddings
+ )
+ inputs_embeds = inputs_embeds.masked_scatter(special_image_mask, patch_embeddings)
+
+ outputs = self.language_model(
+ inputs_embeds=inputs_embeds,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ return outputs
+
+
+@auto_docstring(
+ custom_intro="""
+ Fuyu Model with a language modeling head on top for causal language model conditioned on image patches and text.
+ """
+)
+class FuyuForCausalLM(FuyuPreTrainedModel, GenerationMixin):
+ _tied_weights_keys = {"lm_head.weight": "model.language_model.embed_tokens.weight"}
+
+ def __init__(self, config: FuyuConfig):
+ super().__init__(config)
+ self.model = FuyuModel(config)
+ self.lm_head = nn.Linear(config.text_config.hidden_size, config.text_config.vocab_size, bias=False)
+ self.post_init()
+
+ def get_input_embeddings(self):
+ return self.model.get_input_embeddings()
+
+ def set_input_embeddings(self, value):
+ self.model.set_input_embeddings(value)
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ # [batch_size, num_total_patches, patch_size_ x patch_size x num_channels ]
+ image_patches: torch.Tensor | None = None,
+ image_patches_indices: torch.Tensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ use_cache: bool | None = None,
+ labels: torch.Tensor | None = None,
+ logits_to_keep: int | None = 0,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | CausalLMOutputWithPast:
+ r"""
+ image_patches (`torch.FloatTensor` of shape `(batch_size, num_total_patches, patch_size_ x patch_size x num_channels)`, *optional*):
+ Image patches to be used as continuous embeddings. The patches are flattened and then projected to the
+ hidden size of the model.
+ image_patches_indices (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*):
+ Tensor of indices of the image patches in the input_ids tensor.
+ 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.text_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.text_config.vocab_size]`.
+
+ Examples:
+
+ ```python
+ >>> from transformers import FuyuProcessor, FuyuForCausalLM
+ >>> from PIL import Image
+ >>> import httpx
+ >>> from io import BytesIO
+
+ >>> processor = FuyuProcessor.from_pretrained("adept/fuyu-8b")
+ >>> model = FuyuForCausalLM.from_pretrained("adept/fuyu-8b")
+
+ >>> url = "https://huggingface.co/datasets/hf-internal-testing/fixtures-captioning/resolve/main/bus.png"
+ >>> with httpx.stream("GET", url) as response:
+ ... image = Image.open(BytesIO(response.read()))
+ >>> prompt = "Generate a coco-style caption.\n"
+
+ >>> inputs = processor(images=image, text=prompt, return_tensors="pt")
+ >>> outputs = model(**inputs)
+
+ >>> generated_ids = model.generate(**inputs, max_new_tokens=7)
+ >>> generation_text = processor.batch_decode(generated_ids[:, -7:], skip_special_tokens=True)
+ >>> print(generation_text[0])
+ A blue bus parked on the side of a road.
+ ```"""
+
+ outputs = self.model(
+ input_ids=input_ids,
+ image_patches=image_patches,
+ image_patches_indices=image_patches_indices,
+ inputs_embeds=inputs_embeds,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ hidden_states = outputs[0]
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(
+ logits=logits, labels=labels, vocab_size=self.config.text_config.vocab_size, **kwargs
+ )
+
+ return CausalLMOutputWithPast(
+ loss=loss,
+ logits=logits,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ )
+
+ def prepare_inputs_for_generation(
+ self,
+ input_ids,
+ past_key_values=None,
+ attention_mask=None,
+ inputs_embeds=None,
+ image_patches=None,
+ image_patches_indices=None,
+ is_first_iteration=False,
+ **kwargs,
+ ):
+ # Overwritten -- in specific circumstances we don't want to forward image inputs to the model
+
+ model_inputs = super().prepare_inputs_for_generation(
+ input_ids,
+ past_key_values=past_key_values,
+ attention_mask=attention_mask,
+ inputs_embeds=inputs_embeds,
+ image_patches=image_patches,
+ image_patches_indices=image_patches_indices,
+ is_first_iteration=is_first_iteration,
+ **kwargs,
+ )
+
+ if not is_first_iteration and kwargs.get("use_cache", True):
+ # set image_patches and image_patches_indices to `None` for decoding stage
+ model_inputs["image_patches_indices"] = None
+ model_inputs["image_patches"] = None
+
+ return model_inputs
+
+
+__all__ = ["FuyuForCausalLM", "FuyuPreTrainedModel", "FuyuModel"]
diff --git a/third_party/transformers/src/transformers/models/fuyu/processing_fuyu.py b/third_party/transformers/src/transformers/models/fuyu/processing_fuyu.py
new file mode 100644
index 0000000000000000000000000000000000000000..76287ae3a5ea59913c0780727548346dad30c670
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/fuyu/processing_fuyu.py
@@ -0,0 +1,771 @@
+# Copyright 2023 The HuggingFace Inc. team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Image/Text processor class for GIT
+"""
+
+import re
+from typing import Union
+
+import numpy as np
+
+from ...image_utils import ImageInput
+from ...processing_utils import (
+ MultiModalData,
+ ProcessingKwargs,
+ ProcessorMixin,
+ Unpack,
+)
+from ...tokenization_utils_base import PreTokenizedInput, TextInput
+from ...utils import auto_docstring, is_torch_available, logging, requires_backends
+from ...utils.import_utils import requires
+
+
+if is_torch_available():
+ from .image_processing_fuyu import FuyuBatchFeature
+
+
+logger = logging.get_logger(__name__)
+
+
+if is_torch_available():
+ import torch
+
+
+TEXT_REPR_BBOX_OPEN = ""
+TEXT_REPR_BBOX_CLOSE = ""
+TEXT_REPR_POINT_OPEN = ""
+TEXT_REPR_POINT_CLOSE = ""
+
+TOKEN_BBOX_OPEN_STRING = "<0x00>" #
+TOKEN_BBOX_CLOSE_STRING = "<0x01>" #
+TOKEN_POINT_OPEN_STRING = "<0x02>" #
+TOKEN_POINT_CLOSE_STRING = "<0x03>" #
+BEGINNING_OF_ANSWER_STRING = "<0x04>" #
+
+
+class FuyuProcessorKwargs(ProcessingKwargs, total=False):
+ _defaults = {
+ "text_kwargs": {
+ "add_special_tokens": True,
+ "padding": False,
+ "stride": 0,
+ "return_attention_mask": True,
+ "return_overflowing_tokens": False,
+ "return_special_tokens_mask": False,
+ "return_offsets_mapping": False,
+ "return_token_type_ids": False,
+ "return_length": False,
+ "verbose": True,
+ "return_mm_token_type_ids": False,
+ },
+ }
+
+
+def full_unpacked_stream_to_tensor(
+ all_bi_tokens_to_place: list[int],
+ full_unpacked_stream: list["torch.Tensor"],
+ fill_value: int,
+ batch_size: int,
+ new_seq_len: int,
+ offset: int,
+) -> "torch.Tensor":
+ """Takes an unpacked stream of tokens (i.e. a list of tensors, one for each item in the batch) and does
+ the required padding to create a single tensor for the batch of shape batch_size x new_seq_len.
+ """
+
+ assert len(all_bi_tokens_to_place) == batch_size
+ assert len(full_unpacked_stream) == batch_size
+
+ # Create padded tensors for the full batch.
+ new_padded_tensor = torch.full(
+ [batch_size, new_seq_len],
+ fill_value=fill_value,
+ dtype=full_unpacked_stream[0].dtype,
+ device=full_unpacked_stream[0].device,
+ )
+
+ # Place each batch entry into the batch tensor.
+ for bi in range(batch_size):
+ tokens_to_place = all_bi_tokens_to_place[bi]
+ new_padded_tensor[bi, :tokens_to_place] = full_unpacked_stream[bi][offset : tokens_to_place + offset]
+
+ return new_padded_tensor
+
+
+def construct_full_unpacked_stream(
+ num_real_text_tokens: Union[list[list[int]], "torch.Tensor"],
+ input_stream: "torch.Tensor",
+ image_tokens: list[list["torch.Tensor"]],
+ batch_size: int,
+ num_sub_sequences: int,
+) -> list["torch.Tensor"]:
+ """Takes an input_stream tensor of shape B x S x ?. For each subsequence, adds any required
+ padding to account for images and then unpacks the subsequences to create a single sequence per item in the batch.
+ Returns a list of tensors, one for each item in the batch."""
+
+ all_bi_stream = []
+
+ for batch_index in range(batch_size):
+ all_si_stream = []
+
+ # First, construct full token stream (including image placeholder tokens) and loss mask for each subsequence
+ # and append to lists. We use lists rather than tensors because each subsequence is variable-sized.
+ # TODO Remove this logic in a subsequent release since subsequences are not supported.
+ image_adjustment = image_tokens[batch_index][0]
+ subsequence_stream = torch.cat([image_adjustment, input_stream[batch_index, 0]], dim=0)
+ num_real_tokens = image_adjustment.shape[0] + num_real_text_tokens[batch_index][0]
+ all_si_stream.append(subsequence_stream[:num_real_tokens])
+ all_bi_stream.append(torch.cat(all_si_stream, dim=0))
+
+ return all_bi_stream
+
+
+def _replace_string_repr_with_token_tags(prompt: str) -> str:
+ prompt = prompt.replace(TEXT_REPR_POINT_OPEN, TOKEN_POINT_OPEN_STRING)
+ prompt = prompt.replace(TEXT_REPR_POINT_CLOSE, TOKEN_POINT_CLOSE_STRING)
+ prompt = prompt.replace(TEXT_REPR_BBOX_OPEN, TOKEN_BBOX_OPEN_STRING)
+ prompt = prompt.replace(TEXT_REPR_BBOX_CLOSE, TOKEN_BBOX_CLOSE_STRING)
+ return prompt
+
+
+def _segment_prompt_into_text_token_conversions(prompt: str) -> list:
+ """
+ Given a string prompt, converts the prompt into a list of TextTokenConversions.
+ """
+ # Wherever, we notice the [TOKEN_OPEN_STRING, TOKEN_CLOSE_STRING], we split the prompt
+ prompt_text_list: list = []
+ regex_pattern = re.compile(
+ f"({TOKEN_BBOX_OPEN_STRING}|{TOKEN_BBOX_CLOSE_STRING}|{TOKEN_POINT_OPEN_STRING}|{TOKEN_POINT_CLOSE_STRING})"
+ )
+ # Split by the regex pattern
+ prompt_split = regex_pattern.split(prompt)
+ for i, elem in enumerate(prompt_split):
+ if len(elem) == 0 or elem in [
+ TOKEN_BBOX_OPEN_STRING,
+ TOKEN_BBOX_CLOSE_STRING,
+ TOKEN_POINT_OPEN_STRING,
+ TOKEN_POINT_CLOSE_STRING,
+ ]:
+ continue
+ prompt_text_list.append(
+ (elem, i > 1 and prompt_split[i - 1] in [TOKEN_BBOX_OPEN_STRING, TOKEN_POINT_OPEN_STRING])
+ )
+ return prompt_text_list
+
+
+def _transform_coordinates_and_tokenize(prompt: str, scale_factor: float, tokenizer) -> list[int]:
+ """
+ This function transforms the prompt in the following fashion:
+ - and to their respective token mappings
+ - extract the coordinates from the tag
+ - transform the coordinates into the transformed image space
+ - return the prompt tokens with the transformed coordinates and new tags
+
+ Bounding boxes and points MUST be in the following format: y1, x1, y2, x2 x, y The spaces
+ and punctuation added above are NOT optional.
+ """
+ # Make a namedtuple that stores "text" and "is_bbox"
+
+ # We want to do the following: Tokenize the code normally -> when we see a point or box, tokenize using the tokenize_within_tag function
+ # When point or box close tag, continue tokenizing normally
+ # First, we replace the point and box tags with their respective tokens
+ prompt = _replace_string_repr_with_token_tags(prompt)
+ # Tokenize the prompt
+ # Convert prompt into a list split
+ prompt_text_list = _segment_prompt_into_text_token_conversions(prompt)
+ transformed_prompt_tokens: list[int] = []
+ for elem in prompt_text_list:
+ if elem[1]:
+ # This is a location, we need to tokenize it
+ within_tag_tokenized = _transform_within_tags(elem[0], scale_factor, tokenizer)
+ # Surround the text with the open and close tags
+ transformed_prompt_tokens.extend(within_tag_tokenized)
+ else:
+ transformed_prompt_tokens.extend(tokenizer(elem[0], add_special_tokens=False).input_ids)
+ return transformed_prompt_tokens
+
+
+def _transform_within_tags(text: str, scale_factor: float, tokenizer) -> list[int]:
+ """
+ Given a bounding box of the fashion 1, 2, 3, 4 | 1, 2 This function is responsible for
+ converting 1, 2, 3, 4 into tokens of 1 2 3 4 without any commas.
+ """
+ # Convert the text into a list of strings.
+ num_int_strs = text.split(",")
+ if len(num_int_strs) == 2:
+ # If there are any open or close tags, remove them.
+ token_space_open_string = tokenizer.vocab[TOKEN_POINT_OPEN_STRING]
+ token_space_close_string = tokenizer.vocab[TOKEN_POINT_CLOSE_STRING]
+ else:
+ token_space_open_string = tokenizer.vocab[TOKEN_BBOX_OPEN_STRING]
+ token_space_close_string = tokenizer.vocab[TOKEN_BBOX_CLOSE_STRING]
+
+ # Remove all spaces from num_ints
+ num_ints = [float(num.strip()) for num in num_int_strs]
+ # scale to transformed image size
+ if len(num_ints) == 2:
+ num_ints_translated = scale_point_to_transformed_image(x=num_ints[0], y=num_ints[1], scale_factor=scale_factor)
+ elif len(num_ints) == 4:
+ num_ints_translated = scale_bbox_to_transformed_image(
+ top=num_ints[0],
+ left=num_ints[1],
+ bottom=num_ints[2],
+ right=num_ints[3],
+ scale_factor=scale_factor,
+ )
+ else:
+ raise ValueError(f"Invalid number of ints: {len(num_ints)}")
+ # Tokenize the text, skipping the
+ tokens = [tokenizer.vocab[str(num)] for num in num_ints_translated]
+ return [token_space_open_string] + tokens + [token_space_close_string]
+
+
+def _tokenize_prompts_with_image_and_batch(
+ tokenizer,
+ prompts: list[list[str]],
+ scale_factors: list[list["torch.Tensor"]] | None,
+ max_tokens_to_generate: int,
+ max_position_embeddings: int,
+ add_BOS: bool, # Same issue with types as above
+ add_beginning_of_answer_token: bool,
+) -> tuple["torch.Tensor", "torch.Tensor"]:
+ """
+ Given a set of prompts and number of tokens to generate:
+ - tokenize prompts
+ - set the sequence length to be the max of length of prompts plus the number of tokens we would like to generate
+ - pad all the sequences to this length so we can convert them into a 3D tensor.
+ """
+
+ # If not tool use, transform the coordinates while tokenizing
+ if scale_factors is not None:
+ transformed_prompt_tokens = []
+ for prompt_seq, scale_factor_seq in zip(prompts, scale_factors):
+ transformed_prompt_tokens.append(
+ [
+ _transform_coordinates_and_tokenize(prompt, scale_factor.item(), tokenizer)
+ for prompt, scale_factor in zip(prompt_seq, scale_factor_seq)
+ ]
+ )
+ else:
+ transformed_prompt_tokens = [[tokenizer.tokenize(prompt) for prompt in prompt_seq] for prompt_seq in prompts]
+
+ prompts_tokens = transformed_prompt_tokens
+
+ if add_BOS:
+ bos_token = tokenizer.vocab[""]
+ else:
+ bos_token = tokenizer.vocab["|ENDOFTEXT|"]
+ prompts_tokens = [[[bos_token] + x for x in prompt_seq] for prompt_seq in prompts_tokens]
+ if add_beginning_of_answer_token:
+ beginning_of_answer = tokenizer.vocab[BEGINNING_OF_ANSWER_STRING]
+ # Only add bbox open token to the last subsequence since that is what will be completed
+ for token_seq in prompts_tokens:
+ token_seq[-1].append(beginning_of_answer)
+
+ # Now we have a list of list of tokens which each list has a different
+ # size. We want to extend this list to:
+ # - incorporate the tokens that need to be generated
+ # - make all the sequences equal length.
+ # Get the prompts length.
+
+ prompts_length = [[len(x) for x in prompts_tokens_seq] for prompts_tokens_seq in prompts_tokens]
+ # Get the max prompts length.
+ max_prompt_len: int = np.max(prompts_length)
+ # Number of tokens in the each sample of the batch.
+ samples_length = min(max_prompt_len + max_tokens_to_generate, max_position_embeddings)
+ if max_prompt_len + max_tokens_to_generate > max_position_embeddings:
+ logger.warning(
+ f"Max subsequence prompt length of {max_prompt_len} + max tokens to generate {max_tokens_to_generate}",
+ f"exceeds context length of {max_position_embeddings}. Will generate as many tokens as possible.",
+ )
+ # Now update the list of list to be of the same size: samples_length.
+ for prompt_tokens_seq, prompts_length_seq in zip(prompts_tokens, prompts_length):
+ for prompt_tokens, prompt_length in zip(prompt_tokens_seq, prompts_length_seq):
+ if len(prompt_tokens) > samples_length:
+ raise ValueError("Length of subsequence prompt exceeds sequence length.")
+ padding_size = samples_length - prompt_length
+ prompt_tokens.extend([tokenizer.vocab["|ENDOFTEXT|"]] * padding_size)
+
+ # Now we are in a structured format, we can convert to tensors.
+ prompts_tokens_tensor = torch.tensor(prompts_tokens, dtype=torch.int64)
+ prompts_length_tensor = torch.tensor(prompts_length, dtype=torch.int64)
+
+ return prompts_tokens_tensor, prompts_length_tensor
+
+
+# Simplified assuming self.crop_top = self.padding_top = 0
+def original_to_transformed_h_coords(original_coords, scale_h):
+ return np.round(original_coords * scale_h).astype(np.int32)
+
+
+# Simplified assuming self.crop_left = self.padding_left = 0
+def original_to_transformed_w_coords(original_coords, scale_w):
+ return np.round(original_coords * scale_w).astype(np.int32)
+
+
+def scale_point_to_transformed_image(x: float, y: float, scale_factor: float) -> list[int]:
+ x_scaled = original_to_transformed_w_coords(np.array([x / 2]), scale_factor)[0]
+ y_scaled = original_to_transformed_h_coords(np.array([y / 2]), scale_factor)[0]
+ return [x_scaled, y_scaled]
+
+
+def scale_bbox_to_transformed_image(
+ top: float, left: float, bottom: float, right: float, scale_factor: float
+) -> list[int]:
+ top_scaled = original_to_transformed_w_coords(np.array([top / 2]), scale_factor)[0]
+ left_scaled = original_to_transformed_h_coords(np.array([left / 2]), scale_factor)[0]
+ bottom_scaled = original_to_transformed_w_coords(np.array([bottom / 2]), scale_factor)[0]
+ right_scaled = original_to_transformed_h_coords(np.array([right / 2]), scale_factor)[0]
+ return [top_scaled, left_scaled, bottom_scaled, right_scaled]
+
+
+@requires(backends=("vision",))
+@auto_docstring
+class FuyuProcessor(ProcessorMixin):
+ @classmethod
+ def _load_tokenizer_from_pretrained(
+ cls, sub_processor_type, pretrained_model_name_or_path, subfolder="", **kwargs
+ ):
+ """
+ Override for BC. Fuyu uses TokenizersBackend and requires token_type_ids to be removed from model_input_names
+ because Fuyu uses mm_token_type_ids instead for multimodal token identification. `
+ """
+ from ...tokenization_utils_tokenizers import TokenizersBackend
+
+ tokenizer = TokenizersBackend.from_pretrained(pretrained_model_name_or_path, **kwargs)
+ # Remove token_type_ids as Fuyu uses mm_token_type_ids instead
+ if "token_type_ids" in tokenizer.model_input_names:
+ tokenizer.model_input_names.remove("token_type_ids")
+ return tokenizer
+
+ def __init__(self, image_processor, tokenizer, **kwargs):
+ super().__init__(image_processor=image_processor, tokenizer=tokenizer)
+ self.image_processor = image_processor
+ self.tokenizer = tokenizer
+ self.max_tokens_to_generate = 10
+ self.max_position_embeddings = 16384 # TODO Can't derive this from model files: where to set it?
+ self.pad_token_id = 0
+ self.dummy_image_index = -1
+ self.image_token_id = tokenizer.encode("|SPEAKER|", add_special_tokens=False)[1]
+ self.image_newline_id = tokenizer.encode("|NEWLINE|", add_special_tokens=False)[1]
+ self.image_ids = [self.image_newline_id, self.image_token_id]
+
+ def _left_pad_inputs_with_attention_mask(self, model_inputs: list[dict], return_attention_mask: bool):
+ max_length_input_ids = max(entry["input_ids"].shape[1] for entry in model_inputs)
+ max_length_image_patch_indices = max(entry["image_patches_indices"].shape[1] for entry in model_inputs)
+
+ batched_inputs = {"input_ids": [], "image_patches": [], "image_patches_indices": [], "attention_mask": []}
+
+ for entry in model_inputs:
+ for key, tensor in entry.items():
+ if key == "input_ids":
+ num_padding_tokens = max_length_input_ids - tensor.shape[1]
+ padded_input_ids = torch.cat(
+ [
+ torch.full((tensor.shape[0], num_padding_tokens), self.pad_token_id, dtype=torch.long),
+ tensor,
+ ],
+ dim=1,
+ )
+ batched_inputs[key].append(padded_input_ids)
+
+ attention_mask = torch.cat(
+ [torch.zeros(tensor.shape[0], num_padding_tokens, dtype=torch.long), torch.ones_like(tensor)],
+ dim=1,
+ )
+ batched_inputs["attention_mask"].append(attention_mask)
+
+ elif key == "image_patches":
+ # For image_patches, we don't pad but just append them to the list.
+ batched_inputs[key].append(tensor)
+
+ else: # for image_patches_indices
+ num_padding_indices = max_length_image_patch_indices - tensor.shape[1]
+ padded_indices = torch.cat(
+ [
+ torch.full(
+ (tensor.shape[0], num_padding_indices), self.dummy_image_index, dtype=torch.long
+ ),
+ tensor,
+ ],
+ dim=1,
+ )
+ batched_inputs[key].append(padded_indices)
+ batched_keys = ["input_ids", "image_patches_indices"]
+ if return_attention_mask:
+ batched_keys.append("attention_mask")
+ for key in batched_keys:
+ batched_inputs[key] = torch.cat(batched_inputs[key], dim=0)
+
+ # Cast images to tensor as well, if only one image passed and no padding needed
+ # NOTE: vLLM expects all processor outputs to be a tensor
+ if len(batched_inputs["image_patches"]) == 1:
+ batched_inputs["image_patches"] = torch.cat(batched_inputs["image_patches"], dim=0)
+
+ return batched_inputs
+
+ def get_sample_encoding(
+ self,
+ prompts,
+ scale_factors,
+ image_unpadded_heights,
+ image_unpadded_widths,
+ image_placeholder_id,
+ image_newline_id,
+ tensor_batch_images,
+ ):
+ image_present = torch.ones(1, 1, 1)
+ model_image_input = self.image_processor.preprocess_with_tokenizer_info(
+ image_input=tensor_batch_images,
+ image_present=image_present,
+ image_unpadded_h=image_unpadded_heights,
+ image_unpadded_w=image_unpadded_widths,
+ image_placeholder_id=image_placeholder_id,
+ image_newline_id=image_newline_id,
+ variable_sized=True,
+ )
+ # FIXME max_tokens_to_generate is embedded into this processor's call.
+ prompt_tokens, prompts_length = _tokenize_prompts_with_image_and_batch(
+ tokenizer=self.tokenizer,
+ prompts=prompts,
+ scale_factors=scale_factors,
+ max_tokens_to_generate=self.max_tokens_to_generate,
+ max_position_embeddings=self.max_position_embeddings,
+ add_BOS=True,
+ add_beginning_of_answer_token=True,
+ )
+ image_padded_unpacked_tokens = construct_full_unpacked_stream(
+ num_real_text_tokens=prompts_length,
+ input_stream=prompt_tokens,
+ image_tokens=model_image_input["image_input_ids"],
+ batch_size=1,
+ num_sub_sequences=self.subsequence_length,
+ )
+ # Construct inputs for image patch indices.
+ unpacked_image_patch_indices_per_batch = construct_full_unpacked_stream(
+ num_real_text_tokens=prompts_length,
+ input_stream=torch.full_like(prompt_tokens, -1),
+ image_tokens=model_image_input["image_patch_indices_per_batch"],
+ batch_size=1,
+ num_sub_sequences=self.subsequence_length,
+ )
+ max_prompt_length = max(x.shape[-1] for x in image_padded_unpacked_tokens)
+ max_seq_len_batch = min(max_prompt_length + self.max_tokens_to_generate, self.max_position_embeddings)
+ tokens_to_place = min(max_seq_len_batch, max(0, image_padded_unpacked_tokens[0].shape[0]))
+
+ # Use same packing logic for the image patch indices.
+ image_patch_input_indices = full_unpacked_stream_to_tensor(
+ all_bi_tokens_to_place=[tokens_to_place],
+ full_unpacked_stream=unpacked_image_patch_indices_per_batch,
+ fill_value=-1,
+ batch_size=1,
+ new_seq_len=max_seq_len_batch,
+ offset=0,
+ )
+ image_patches_tensor = torch.stack([img[0] for img in model_image_input["image_patches"]])
+ batch_encoding = {
+ "input_ids": image_padded_unpacked_tokens[0].unsqueeze(0),
+ "image_patches": image_patches_tensor,
+ "image_patches_indices": image_patch_input_indices,
+ }
+ return batch_encoding
+
+ @auto_docstring
+ def __call__(
+ self,
+ images: ImageInput | None = None,
+ text: str | list[str] | TextInput | PreTokenizedInput | None = None,
+ **kwargs: Unpack[FuyuProcessorKwargs],
+ ) -> "FuyuBatchFeature":
+ r"""
+ Returns:
+ [`FuyuBatchEncoding`]: A [`FuyuBatchEncoding`] with the following fields:
+
+ - **input_ids** -- Tensor of token ids to be fed to a model. Returned when `text` is not `None`.
+ - **image_patches** -- List of Tensor of image patches. Returned when `images` is not `None`.
+ - **image_patches_indices** -- Tensor of indices where patch embeddings have to be inserted by the model.
+ - **attention_mask** -- List of indices specifying which tokens should be attended to by the model when
+ `return_attention_mask=True`.
+ """
+ requires_backends(self, ["torch"])
+
+ # --- Check input validity ---
+ if text is None and images is None:
+ raise ValueError("You have to specify either text or images. Both cannot be None.")
+
+ output_kwargs = self._merge_kwargs(
+ FuyuProcessorKwargs,
+ tokenizer_init_kwargs=self.tokenizer.init_kwargs,
+ **kwargs,
+ )
+ return_mm_token_type_ids = output_kwargs["text_kwargs"].pop("return_mm_token_type_ids", False)
+
+ if not output_kwargs["text_kwargs"].setdefault("return_attention_mask", True):
+ raise ValueError("`return_attention_mask=False` is not supported for this model.")
+
+ if text is not None and images is None:
+ logger.warning("You are processing a text with no associated image. Make sure it is intended.")
+ text_encoding = self.tokenizer(text, **output_kwargs["text_kwargs"])
+ return text_encoding
+
+ if text is None and images is not None:
+ logger.warning("You are processing an image with no associated text. Make sure it is intended.")
+ prompts = [[""]]
+ if text is not None and images is not None:
+ if isinstance(text, str):
+ prompts = [[text]]
+ elif isinstance(text, list):
+ prompts = [[text_seq] for text_seq in text]
+
+ # --- Preprocess images using self.image_processor ---
+
+ # FIXME - We hard code "pt" here because the rest of the processing assumes torch tensors
+ output_kwargs["images_kwargs"]["return_tensors"] = "pt"
+ image_encoding = self.image_processor.preprocess(images, **output_kwargs["images_kwargs"])
+ batch_images = image_encoding["images"]
+ image_unpadded_heights = image_encoding["image_unpadded_heights"]
+ image_unpadded_widths = image_encoding["image_unpadded_widths"]
+ scale_factors = image_encoding["image_scale_factors"]
+ self.subsequence_length = 1 # Each batch contains only one sequence.
+ self.batch_size = len(batch_images)
+
+ # --- Use self.tokenizer to get the ids of special tokens to insert into image ids ---
+
+ tensor_batch_images = torch.stack([img[0] for img in batch_images if img]).unsqueeze(1)
+
+ # --- Use self.image_processor again to obtain the full token ids and batch inputs ---
+ all_encodings = []
+
+ for prompt, scale_factor, image_unpadded_height, image_unpadded_width, tensor_batch_image in zip(
+ prompts, scale_factors, image_unpadded_heights, image_unpadded_widths, tensor_batch_images
+ ):
+ sample_encoding = self.get_sample_encoding(
+ prompts=[prompt],
+ scale_factors=[scale_factor],
+ image_unpadded_heights=torch.tensor([image_unpadded_height]),
+ image_unpadded_widths=torch.tensor([image_unpadded_width]),
+ image_placeholder_id=self.image_token_id,
+ image_newline_id=self.image_newline_id,
+ tensor_batch_images=tensor_batch_image.unsqueeze(0),
+ )
+ all_encodings.append(sample_encoding)
+
+ batch_encoding = self._left_pad_inputs_with_attention_mask(
+ model_inputs=all_encodings, return_attention_mask=True
+ )
+ if return_mm_token_type_ids:
+ batch_encoding["mm_token_type_ids"] = self.create_mm_token_type_ids(batch_encoding["input_ids"])
+ batch_encoding["mm_token_type_ids"] = torch.tensor(batch_encoding["mm_token_type_ids"])
+ return FuyuBatchFeature(data=batch_encoding)
+
+ def _get_num_multimodal_tokens(self, image_sizes=None, **kwargs):
+ """
+ Computes the number of placeholder tokens needed for multimodal inputs with the given sizes.
+
+ Args:
+ image_sizes (`list[list[int]]`, *optional*):
+ The input sizes formatted as (height, width) per each image.
+
+ Returns:
+ `MultiModalData`: A `MultiModalData` object holding number of tokens per each of the provided
+ input modalities, along with other useful data.
+ """
+
+ vision_data = {}
+ if image_sizes is not None:
+ size = kwargs.get("size") or self.image_processor.size
+ padded_height, padded_width = size["height"], size["width"]
+
+ num_image_tokens = []
+ num_image_patches = [1] * len(image_sizes)
+ for image_size in image_sizes:
+ height_scale_factor = padded_height / image_size[0]
+ width_scale_factor = padded_width / image_size[1]
+ optimal_scale_factor = min(height_scale_factor, width_scale_factor)
+
+ image_unpadded_h = min(int(image_size[0] * optimal_scale_factor), image_size[0])
+ image_unpadded_w = min(int(image_size[1] * optimal_scale_factor), image_size[1])
+
+ # We can use torch here because Fuyu processor has hard dependency on torch. NOTE: Fuyu can't do multi-image
+ # thus the below (1, 1, 1) is hardcoded. Same as when calling the processor
+ model_image_input = self.image_processor.preprocess_with_tokenizer_info(
+ image_input=torch.zeros(1, 1, 3, padded_height, padded_width),
+ image_present=torch.ones(1, 1, 1),
+ image_unpadded_h=torch.tensor([[image_unpadded_h]]),
+ image_unpadded_w=torch.tensor([[image_unpadded_w]]),
+ image_placeholder_id=0, # dummy ids, we can be sure `id=0` is never out-of-range
+ image_newline_id=0,
+ variable_sized=True,
+ )
+ num_image_tokens.append(model_image_input["image_input_ids"][0][0].shape[-1])
+ vision_data.update({"num_image_tokens": num_image_tokens, "num_image_patches": num_image_patches})
+ return MultiModalData(**vision_data)
+
+ def post_process_box_coordinates(self, outputs, target_sizes=None):
+ """
+ Transforms raw coordinates detected by [`FuyuForCausalLM`] to the original images' coordinate space.
+ Coordinates will be returned in "box" format, with the following pattern:
+ `top, left, bottom, right`
+
+ Point coordinates are not supported yet.
+
+ Args:
+ outputs ([`GenerateOutput`]):
+ Raw outputs from `generate`.
+ target_sizes (`torch.Tensor`, *optional*):
+ Tensor of shape (batch_size, 2) where each entry is the (height, width) of the corresponding image in
+ the batch. If set, found coordinates in the output sequence are rescaled to the target sizes. If left
+ to None, coordinates will not be rescaled.
+
+ Returns:
+ `GenerateOutput`: Same output type returned by `generate`, with output token ids replaced with
+ boxed and possible rescaled coordinates.
+ """
+
+ def scale_factor_to_fit(original_size, target_size=None):
+ height, width = original_size
+ if target_size is None:
+ max_height = self.image_processor.size["height"]
+ max_width = self.image_processor.size["width"]
+ else:
+ max_height, max_width = target_size
+ if width <= max_width and height <= max_height:
+ return 1.0
+ return min(max_height / height, max_width / width)
+
+ def find_delimiters_pair(tokens, start_token, end_token):
+ start_id = self.tokenizer.convert_tokens_to_ids(start_token)
+ end_id = self.tokenizer.convert_tokens_to_ids(end_token)
+
+ starting_positions = (tokens == start_id).nonzero(as_tuple=True)[0]
+ ending_positions = (tokens == end_id).nonzero(as_tuple=True)[0]
+
+ if torch.any(starting_positions) and torch.any(ending_positions):
+ return (starting_positions[0], ending_positions[0])
+ return (None, None)
+
+ def tokens_to_boxes(tokens, original_size):
+ while (pair := find_delimiters_pair(tokens, TOKEN_BBOX_OPEN_STRING, TOKEN_BBOX_CLOSE_STRING)) != (
+ None,
+ None,
+ ):
+ start, end = pair
+ if end != start + 5:
+ continue
+
+ # Retrieve transformed coordinates from tokens
+ coords = self.tokenizer.convert_ids_to_tokens(tokens[start + 1 : end])
+
+ # Scale back to original image size and multiply by 2
+ scale = scale_factor_to_fit(original_size)
+ top, left, bottom, right = [2 * int(float(c) / scale) for c in coords]
+
+ # Replace the IDs so they get detokenized right
+ replacement = f" {TEXT_REPR_BBOX_OPEN}{top}, {left}, {bottom}, {right}{TEXT_REPR_BBOX_CLOSE}"
+ replacement = self.tokenizer.tokenize(replacement)[1:]
+ replacement = self.tokenizer.convert_tokens_to_ids(replacement)
+ replacement = torch.tensor(replacement).to(tokens)
+
+ tokens = torch.cat([tokens[:start], replacement, tokens[end + 1 :]], 0)
+ return tokens
+
+ def tokens_to_points(tokens, original_size):
+ while (pair := find_delimiters_pair(tokens, TOKEN_POINT_OPEN_STRING, TOKEN_POINT_CLOSE_STRING)) != (
+ None,
+ None,
+ ):
+ start, end = pair
+ if end != start + 3:
+ continue
+
+ # Retrieve transformed coordinates from tokens
+ coords = self.tokenizer.convert_ids_to_tokens(tokens[start + 1 : end])
+
+ # Scale back to original image size and multiply by 2
+ scale = scale_factor_to_fit(original_size)
+ x, y = [2 * int(float(c) / scale) for c in coords]
+
+ # Replace the IDs so they get detokenized right
+ replacement = f" {TEXT_REPR_POINT_OPEN}{x}, {y}{TEXT_REPR_POINT_CLOSE}"
+ replacement = self.tokenizer.tokenize(replacement)[1:]
+ replacement = self.tokenizer.convert_tokens_to_ids(replacement)
+ replacement = torch.tensor(replacement).to(tokens)
+
+ tokens = torch.cat([tokens[:start], replacement, tokens[end + 1 :]], 0)
+ return tokens
+
+ if target_sizes is None:
+ target_sizes = ((self.image_processor.size["height"], self.image_processor.size["width"]),) * len(outputs)
+ elif target_sizes.shape[1] != 2:
+ raise ValueError("Each element of target_sizes must contain the size (h, w) of each image of the batch")
+
+ if len(outputs) != len(target_sizes):
+ raise ValueError("Make sure that you pass in as many target sizes as output sequences")
+
+ results = []
+ for seq, size in zip(outputs, target_sizes):
+ seq = tokens_to_boxes(seq, size)
+ seq = tokens_to_points(seq, size)
+ results.append(seq)
+
+ return results
+
+ def post_process_image_text_to_text(self, generated_outputs, skip_special_tokens=True, **kwargs):
+ """
+ Post-processes the output of `FuyuForConditionalGeneration` to only return the text output.
+
+ Args:
+ generated_outputs (`torch.Tensor` or `np.ndarray`):
+ The output of the model. The output is expected to be a tensor of shape `(batch_size, sequence_length)`
+ containing the token ids of the generated sequences.
+ skip_special_tokens (`bool`, *optional*, defaults to `True`):
+ Whether or not to remove special tokens in the output. Argument passed to the tokenizer's `batch_decode` method.
+ **kwargs:
+ Additional arguments to be passed to the tokenizer's `batch_decode method`.
+
+ Returns:
+ `list[str]`: The decoded text output.
+ """
+ beginning_of_answer = self.tokenizer.convert_tokens_to_ids(BEGINNING_OF_ANSWER_STRING)
+ # get boa index for each outputted sequence tensor
+ # start all generated sequences from the beginning of the answer token, pad to have consistent length
+ unpadded_output_sequences = [
+ seq[(seq == beginning_of_answer).nonzero(as_tuple=True)[0] + 1 :] for seq in generated_outputs
+ ]
+ max_len = max(len(seq) for seq in unpadded_output_sequences)
+ # convert to torch and pad sequences
+ padded_output_sequences = torch.full((len(unpadded_output_sequences), max_len), self.pad_token_id)
+ for i, seq in enumerate(unpadded_output_sequences):
+ padded_output_sequences[i, : len(seq)] = torch.tensor(seq)
+
+ return self.batch_decode(padded_output_sequences, skip_special_tokens=skip_special_tokens, **kwargs)
+
+ @property
+ def model_input_names(self):
+ tokenizer_input_names = self.tokenizer.model_input_names
+ image_processor_input_names = self.image_processor.model_input_names
+
+ # Make a copy of list when removing otherwise `self.image_processor.model_input_names` is also modified
+ extra_image_inputs = [
+ "image_input_ids",
+ "image_patch_indices_per_subsequence",
+ "images",
+ "image_patch_indices_per_batch",
+ ]
+ image_processor_input_names = [name for name in image_processor_input_names if name not in extra_image_inputs]
+ return list(tokenizer_input_names + image_processor_input_names + ["image_patches_indices"])
+
+
+__all__ = ["FuyuProcessor"]
diff --git a/third_party/transformers/src/transformers/models/git/__init__.py b/third_party/transformers/src/transformers/models/git/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..06e3e86927ab7901f1302a87882c4f841f35865d
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/git/__init__.py
@@ -0,0 +1,28 @@
+# Copyright 2024 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_git import *
+ from .modeling_git import *
+ from .processing_git import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/git/configuration_git.py b/third_party/transformers/src/transformers/models/git/configuration_git.py
new file mode 100644
index 0000000000000000000000000000000000000000..5311351b61bc5e49fe95032be5320957dc2d66a7
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/git/configuration_git.py
@@ -0,0 +1,113 @@
+# Copyright 2022 The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...utils import auto_docstring, logging
+
+
+logger = logging.get_logger(__name__)
+
+
+@auto_docstring(checkpoint="microsoft/git-base")
+@strict
+class GitVisionConfig(PreTrainedConfig):
+ r"""
+ Example:
+
+ ```python
+ >>> from transformers import GitVisionConfig, GitVisionModel
+
+ >>> # Initializing a GitVisionConfig with microsoft/git-base style configuration
+ >>> configuration = GitVisionConfig()
+
+ >>> # Initializing a GitVisionModel (with random weights) from the microsoft/git-base style configuration
+ >>> model = GitVisionModel(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "git_vision_model"
+ base_config_key = "vision_config"
+
+ hidden_size: int = 768
+ intermediate_size: int = 3072
+ num_hidden_layers: int = 12
+ num_attention_heads: int = 12
+ num_channels: int = 3
+ image_size: int | list[int] | tuple[int, int] = 224
+ patch_size: int | list[int] | tuple[int, int] = 16
+ hidden_act: str = "quick_gelu"
+ layer_norm_eps: float = 1e-5
+ attention_dropout: float | int = 0.0
+ initializer_range: float = 0.02
+
+
+@auto_docstring(checkpoint="microsoft/git-base")
+@strict
+class GitConfig(PreTrainedConfig):
+ r"""
+ num_image_with_embedding (`int`, *optional*):
+ The number of temporal embeddings to add, in case the model is used for video captioning/VQA.
+
+ Examples:
+
+ ```python
+ >>> from transformers import GitConfig, GitModel
+
+ >>> # Initializing a GIT microsoft/git-base style configuration
+ >>> configuration = GitConfig()
+
+ >>> # Initializing a model (with random weights) from the microsoft/git-base style configuration
+ >>> model = GitModel(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "git"
+ sub_configs = {"vision_config": GitVisionConfig}
+
+ vision_config: dict | GitVisionConfig | None = None
+ vocab_size: int = 30522
+ hidden_size: int = 768
+ num_hidden_layers: int = 6
+ num_attention_heads: int = 12
+ intermediate_size: int = 3072
+ hidden_act: str = "gelu"
+ hidden_dropout_prob: float | int = 0.1
+ attention_probs_dropout_prob: float | int = 0.1
+ max_position_embeddings: int = 1024
+ initializer_range: float = 0.02
+ layer_norm_eps: float = 1e-12
+ pad_token_id: int | None = 0
+ use_cache: bool = True
+ tie_word_embeddings: bool = False
+ bos_token_id: int | None = 101
+ eos_token_id: int | list[int] | None = 102
+ num_image_with_embedding: int | None = None
+
+ def __post_init__(self, **kwargs):
+ if self.vision_config is None:
+ self.vision_config = GitVisionConfig()
+ logger.info("vision_config is None. initializing the GitVisionConfig with default values.")
+ elif isinstance(self.vision_config, dict):
+ self.vision_config = GitVisionConfig(**self.vision_config)
+ super().__post_init__(**kwargs)
+
+
+__all__ = ["GitConfig", "GitVisionConfig"]
diff --git a/third_party/transformers/src/transformers/models/git/convert_git_to_pytorch.py b/third_party/transformers/src/transformers/models/git/convert_git_to_pytorch.py
new file mode 100644
index 0000000000000000000000000000000000000000..0ea9f673ca003bd9dde12c24768cb3f2510d7430
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/git/convert_git_to_pytorch.py
@@ -0,0 +1,449 @@
+# Copyright 2022 The HuggingFace Inc. team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Convert GIT checkpoints from the original repository.
+
+URL: https://github.com/microsoft/GenerativeImage2Text/tree/main"""
+
+import argparse
+from io import BytesIO
+from pathlib import Path
+
+import av
+import httpx
+import numpy as np
+import torch
+from huggingface_hub import hf_hub_download
+from PIL import Image
+from torchvision.transforms import CenterCrop, Compose, Normalize, Resize, ToTensor
+
+from transformers import (
+ AutoTokenizer,
+ CLIPImageProcessor,
+ GitConfig,
+ GitForCausalLM,
+ GitProcessor,
+ GitVisionConfig,
+ VideoMAEImageProcessor,
+)
+from transformers.utils import logging
+
+
+logging.set_verbosity_info()
+logger = logging.get_logger(__name__)
+
+
+def get_git_config(model_name):
+ if "base" in model_name and "vqa" in model_name:
+ image_size = 480
+ elif "large" in model_name and "vqa" in model_name:
+ image_size = 420
+ else:
+ image_size = 224
+
+ vision_config = GitVisionConfig(image_size=image_size)
+
+ if "large" in model_name:
+ vision_config.patch_size = 14
+ vision_config.hidden_size = 1024
+ vision_config.intermediate_size = 4096
+ vision_config.num_hidden_layers = 24
+ vision_config.num_attention_heads = 16
+
+ is_video = "vatex" in model_name or "msrvtt" in model_name
+ num_image_with_embedding = 6 if is_video else None
+ config = GitConfig(vision_config=vision_config.to_dict(), num_image_with_embedding=num_image_with_embedding)
+
+ return config, image_size, is_video
+
+
+# here we list all keys to be renamed (original name on the left, our name on the right)
+def create_rename_keys(config, prefix=""):
+ rename_keys = []
+
+ # image encoder
+ # ftm: off
+ rename_keys.append(
+ (f"{prefix}image_encoder.class_embedding", "git.image_encoder.vision_model.embeddings.class_embedding")
+ )
+ rename_keys.append(
+ (
+ f"{prefix}image_encoder.positional_embedding",
+ "git.image_encoder.vision_model.embeddings.position_embedding.weight",
+ )
+ )
+ rename_keys.append(
+ (f"{prefix}image_encoder.conv1.weight", "git.image_encoder.vision_model.embeddings.patch_embedding.weight")
+ )
+ rename_keys.append((f"{prefix}image_encoder.ln_pre.weight", "git.image_encoder.vision_model.pre_layrnorm.weight"))
+ rename_keys.append((f"{prefix}image_encoder.ln_pre.bias", "git.image_encoder.vision_model.pre_layrnorm.bias"))
+ rename_keys.append(
+ (f"{prefix}image_encoder.ln_post.weight", "git.image_encoder.vision_model.post_layernorm.weight")
+ )
+ rename_keys.append((f"{prefix}image_encoder.ln_post.bias", "git.image_encoder.vision_model.post_layernorm.bias"))
+ # fmt: on
+ rename_keys.append((f"{prefix}image_encoder.proj", "git.image_encoder.visual_projection.weight"))
+
+ # fmt: off
+ for i in range(config.vision_config.num_hidden_layers):
+ # image encoder layers: output projection, 2 feedforward neural networks and 2 layernorms
+ rename_keys.append((f"{prefix}image_encoder.transformer.resblocks.{i}.attn.out_proj.weight", f"git.image_encoder.vision_model.encoder.layers.{i}.self_attn.out_proj.weight"))
+ rename_keys.append((f"{prefix}image_encoder.transformer.resblocks.{i}.attn.out_proj.bias", f"git.image_encoder.vision_model.encoder.layers.{i}.self_attn.out_proj.bias"))
+ rename_keys.append((f"{prefix}image_encoder.transformer.resblocks.{i}.ln_1.weight", f"git.image_encoder.vision_model.encoder.layers.{i}.layer_norm1.weight"))
+ rename_keys.append((f"{prefix}image_encoder.transformer.resblocks.{i}.ln_1.bias", f"git.image_encoder.vision_model.encoder.layers.{i}.layer_norm1.bias"))
+ rename_keys.append((f"{prefix}image_encoder.transformer.resblocks.{i}.mlp.c_fc.weight", f"git.image_encoder.vision_model.encoder.layers.{i}.mlp.fc1.weight"))
+ rename_keys.append((f"{prefix}image_encoder.transformer.resblocks.{i}.mlp.c_fc.bias", f"git.image_encoder.vision_model.encoder.layers.{i}.mlp.fc1.bias"))
+ rename_keys.append((f"{prefix}image_encoder.transformer.resblocks.{i}.mlp.c_proj.weight", f"git.image_encoder.vision_model.encoder.layers.{i}.mlp.fc2.weight"))
+ rename_keys.append((f"{prefix}image_encoder.transformer.resblocks.{i}.mlp.c_proj.bias", f"git.image_encoder.vision_model.encoder.layers.{i}.mlp.fc2.bias"))
+ rename_keys.append((f"{prefix}image_encoder.transformer.resblocks.{i}.ln_2.weight", f"git.image_encoder.vision_model.encoder.layers.{i}.layer_norm2.weight"))
+ rename_keys.append((f"{prefix}image_encoder.transformer.resblocks.{i}.ln_2.bias", f"git.image_encoder.vision_model.encoder.layers.{i}.layer_norm2.bias"))
+ # fmt: on
+
+ # text decoder
+ # fmt: off
+ rename_keys.append((f"{prefix}textual.embedding.words.weight", "git.embeddings.word_embeddings.weight"))
+ rename_keys.append((f"{prefix}textual.embedding.positions.weight", "git.embeddings.position_embeddings.weight"))
+ rename_keys.append((f"{prefix}textual.visual_projection.0.weight", "git.visual_projection.visual_projection.0.weight"))
+ rename_keys.append((f"{prefix}textual.visual_projection.0.bias", "git.visual_projection.visual_projection.0.bias"))
+ rename_keys.append((f"{prefix}textual.visual_projection.1.weight", "git.visual_projection.visual_projection.1.weight"))
+ rename_keys.append((f"{prefix}textual.visual_projection.1.bias", "git.visual_projection.visual_projection.1.bias"))
+
+ rename_keys.append((f"{prefix}textual.embedding.layer_norm.weight", "git.embeddings.LayerNorm.weight"))
+ rename_keys.append((f"{prefix}textual.embedding.layer_norm.bias", "git.embeddings.LayerNorm.bias"))
+ rename_keys.append((f"{prefix}textual.output.weight", "output.weight"))
+ rename_keys.append((f"{prefix}textual.output.bias", "output.bias"))
+ for i in range(config.num_hidden_layers):
+ rename_keys.append((f"{prefix}textual.transformer.encoder.layer.{i}.attention.self.query.weight", f"git.encoder.layer.{i}.attention.self.query.weight"))
+ rename_keys.append((f"{prefix}textual.transformer.encoder.layer.{i}.attention.self.query.bias", f"git.encoder.layer.{i}.attention.self.query.bias"))
+ rename_keys.append((f"{prefix}textual.transformer.encoder.layer.{i}.attention.self.key.weight", f"git.encoder.layer.{i}.attention.self.key.weight"))
+ rename_keys.append((f"{prefix}textual.transformer.encoder.layer.{i}.attention.self.key.bias", f"git.encoder.layer.{i}.attention.self.key.bias"))
+ rename_keys.append((f"{prefix}textual.transformer.encoder.layer.{i}.attention.self.value.weight", f"git.encoder.layer.{i}.attention.self.value.weight"))
+ rename_keys.append((f"{prefix}textual.transformer.encoder.layer.{i}.attention.self.value.bias", f"git.encoder.layer.{i}.attention.self.value.bias"))
+ rename_keys.append((f"{prefix}textual.transformer.encoder.layer.{i}.attention.output.dense.weight", f"git.encoder.layer.{i}.attention.output.dense.weight"))
+ rename_keys.append((f"{prefix}textual.transformer.encoder.layer.{i}.attention.output.dense.bias", f"git.encoder.layer.{i}.attention.output.dense.bias"))
+ rename_keys.append((f"{prefix}textual.transformer.encoder.layer.{i}.attention.output.LayerNorm.weight", f"git.encoder.layer.{i}.attention.output.LayerNorm.weight"))
+ rename_keys.append((f"{prefix}textual.transformer.encoder.layer.{i}.attention.output.LayerNorm.bias", f"git.encoder.layer.{i}.attention.output.LayerNorm.bias"))
+ rename_keys.append((f"{prefix}textual.transformer.encoder.layer.{i}.intermediate.dense.weight", f"git.encoder.layer.{i}.intermediate.dense.weight"))
+ rename_keys.append((f"{prefix}textual.transformer.encoder.layer.{i}.intermediate.dense.bias", f"git.encoder.layer.{i}.intermediate.dense.bias"))
+ rename_keys.append((f"{prefix}textual.transformer.encoder.layer.{i}.output.dense.weight", f"git.encoder.layer.{i}.output.dense.weight"))
+ rename_keys.append((f"{prefix}textual.transformer.encoder.layer.{i}.output.dense.bias", f"git.encoder.layer.{i}.output.dense.bias"))
+ rename_keys.append((f"{prefix}textual.transformer.encoder.layer.{i}.output.LayerNorm.weight", f"git.encoder.layer.{i}.output.LayerNorm.weight"))
+ rename_keys.append((f"{prefix}textual.transformer.encoder.layer.{i}.output.LayerNorm.bias", f"git.encoder.layer.{i}.output.LayerNorm.bias"))
+ # fmt: on
+
+ if config.num_image_with_embedding is not None:
+ rename_keys.append(("img_temperal_embedding.0", "git.img_temperal_embedding.0"))
+ rename_keys.append(("img_temperal_embedding.1", "git.img_temperal_embedding.1"))
+ rename_keys.append(("img_temperal_embedding.2", "git.img_temperal_embedding.2"))
+ rename_keys.append(("img_temperal_embedding.3", "git.img_temperal_embedding.3"))
+ rename_keys.append(("img_temperal_embedding.4", "git.img_temperal_embedding.4"))
+ rename_keys.append(("img_temperal_embedding.5", "git.img_temperal_embedding.5"))
+
+ return rename_keys
+
+
+def rename_key(dct, old, new):
+ val = dct.pop(old)
+ dct[new] = val.T if "image_encoder.visual_projection" in new else val
+
+
+# we split up the matrix of each CLIP encoder layer into queries, keys and values
+def read_in_q_k_v(state_dict, config, prefix=""):
+ dim = config.vision_config.hidden_size
+ for i in range(config.vision_config.num_hidden_layers):
+ # read in weights + bias of input projection layer (in the original implementation, this is a single matrix + bias)
+ in_proj_weight = state_dict.pop(f"{prefix}image_encoder.transformer.resblocks.{i}.attn.in_proj_weight")
+ in_proj_bias = state_dict.pop(f"{prefix}image_encoder.transformer.resblocks.{i}.attn.in_proj_bias")
+ # next, add query, keys and values (in that order) to the state dict
+ state_dict[f"git.image_encoder.vision_model.encoder.layers.{i}.self_attn.q_proj.weight"] = in_proj_weight[
+ :dim, :
+ ]
+ state_dict[f"git.image_encoder.vision_model.encoder.layers.{i}.self_attn.q_proj.bias"] = in_proj_bias[:dim]
+ state_dict[f"git.image_encoder.vision_model.encoder.layers.{i}.self_attn.k_proj.weight"] = in_proj_weight[
+ dim : dim * 2, :
+ ]
+ state_dict[f"git.image_encoder.vision_model.encoder.layers.{i}.self_attn.k_proj.bias"] = in_proj_bias[
+ dim : dim * 2
+ ]
+ state_dict[f"git.image_encoder.vision_model.encoder.layers.{i}.self_attn.v_proj.weight"] = in_proj_weight[
+ -dim:, :
+ ]
+ state_dict[f"git.image_encoder.vision_model.encoder.layers.{i}.self_attn.v_proj.bias"] = in_proj_bias[-dim:]
+
+
+# We will verify our results on an image
+def prepare_img(model_name):
+ if "textvqa" in model_name:
+ filepath = hf_hub_download(repo_id="nielsr/textvqa-sample", filename="bus.png", repo_type="dataset")
+ image = Image.open(filepath).convert("RGB")
+ else:
+ url = "http://images.cocodataset.org/val2017/000000039769.jpg"
+ with httpx.stream("GET", url) as response:
+ image = Image.open(BytesIO(response.read()))
+
+ return image
+
+
+def prepare_video():
+ def read_video_pyav(container, indices):
+ """
+ Decode the video with PyAV decoder.
+
+ Args:
+ container (`av.container.input.InputContainer`): PyAV container.
+ indices (`list[int]`): List of frame indices to decode.
+
+ Returns:
+ result (np.ndarray): np array of decoded frames of shape (num_frames, height, width, 3).
+ """
+ frames = []
+ container.seek(0)
+ start_index = indices[0]
+ end_index = indices[-1]
+ for i, frame in enumerate(container.decode(video=0)):
+ if i > end_index:
+ break
+ if i >= start_index and i in indices:
+ frames.append(frame)
+ return np.stack([x.to_ndarray(format="rgb24") for x in frames])
+
+ def sample_frame_indices(clip_len, frame_sample_rate, seg_len):
+ """
+ Sample a given number of frame indices from the video.
+
+ Args:
+ clip_len (`int`): Total number of frames to sample.
+ frame_sample_rate (`int`): Sample every n-th frame.
+ seg_len (`int`): Maximum allowed index of sample's last frame.
+
+ Returns:
+ indices (`list[int]`): List of sampled frame indices
+ """
+ converted_len = int(clip_len * frame_sample_rate)
+ end_idx = np.random.randint(converted_len, seg_len)
+ start_idx = end_idx - converted_len
+ indices = np.linspace(start_idx, end_idx, num=clip_len)
+ indices = np.clip(indices, start_idx, end_idx - 1).astype(np.int64)
+ return indices
+
+ # set seed for reproducibility
+ np.random.seed(0)
+
+ file_path = hf_hub_download(repo_id="nielsr/video-demo", filename="eating_spaghetti.mp4", repo_type="dataset")
+ with av.open(file_path) as container:
+ # sample 6 frames
+ num_frames = 6
+ indices = sample_frame_indices(
+ clip_len=num_frames, frame_sample_rate=4, seg_len=container.streams.video[0].frames
+ )
+ frames = read_video_pyav(container, indices)
+
+ return frames
+
+
+@torch.no_grad()
+def convert_git_checkpoint(model_name, pytorch_dump_folder_path, push_to_hub=False):
+ """
+ Copy/paste/tweak model's weights to our GIT structure.
+ """
+
+ model_name_to_url = {
+ "git-base": "https://publicgit.blob.core.windows.net/data/output/GIT_BASE/snapshot/model.pt",
+ "git-base-coco": "https://publicgit.blob.core.windows.net/data/output/GIT_BASE_COCO/snapshot/model.pt",
+ "git-base-textcaps": "https://publicgit.blob.core.windows.net/data/output/GIT_BASE_TEXTCAPS/snapshot/model.pt",
+ "git-base-vqav2": "https://publicgit.blob.core.windows.net/data/output/GIT_BASE_VQAv2/snapshot/model.pt",
+ "git-base-textvqa": "https://publicgit.blob.core.windows.net/data/output/GIT_BASE_TEXTVQA/snapshot/model.pt", # todo
+ "git-base-vatex": "https://publicgit.blob.core.windows.net/data/output/GIT_BASE_VATEX/snapshot/model.pt",
+ "git-base-msrvtt-qa": (
+ "https://publicgit.blob.core.windows.net/data/output/GIT_BASE_MSRVTT_QA/snapshot/model.pt"
+ ),
+ "git-large": "https://publicgit.blob.core.windows.net/data/output/GIT_LARGE/snapshot/model.pt",
+ "git-large-coco": "https://publicgit.blob.core.windows.net/data/output/GIT_LARGE_COCO/snapshot/model.pt",
+ "git-large-textcaps": (
+ "https://publicgit.blob.core.windows.net/data/output/GIT_LARGE_TEXTCAPS/snapshot/model.pt"
+ ),
+ "git-large-vqav2": "https://publicgit.blob.core.windows.net/data/output/GIT_LARGE_VQAv2/snapshot/model.pt",
+ "git-large-textvqa": "https://publicgit.blob.core.windows.net/data/output/GIT_LARGE_TEXTVQA/snapshot/model.pt",
+ "git-large-vatex": "https://publicgit.blob.core.windows.net/data/output/GIT_LARGE_VATEX/snapshot/model.pt",
+ "git-large-msrvtt-qa": (
+ "https://publicgit.blob.core.windows.net/data/output/GIT_LARGE_MSRVTT_QA/snapshot/model.pt"
+ ),
+ "git-large-r": "https://publicgit.blob.core.windows.net/data/output/GIT_LARGE_R/snapshot/model.pt",
+ "git-large-r-coco": "https://publicgit.blob.core.windows.net/data/output/GIT_LARGE_R_COCO/snapshot/model.pt",
+ "git-large-r-textcaps": (
+ "https://publicgit.blob.core.windows.net/data/output/GIT_LARGE_R_TEXTCAPS/snapshot/model.pt"
+ ),
+ }
+
+ model_name_to_path = {
+ "git-large": "/Users/nielsrogge/Documents/GIT/git_large_model.pt",
+ "git-large-coco": "/Users/nielsrogge/Documents/GIT/git_large_coco_model.pt",
+ "git-large-textcaps": "/Users/nielsrogge/Documents/GIT/git_large_textcaps_model.pt",
+ "git-large-vqav2": "/Users/nielsrogge/Documents/GIT/git_large_vqav2_model.pt",
+ "git-large-textvqa": "/Users/nielsrogge/Documents/GIT/git_large_textvqa_model.pt",
+ }
+
+ # define GIT configuration based on model name
+ config, image_size, is_video = get_git_config(model_name)
+ if "large" in model_name and not is_video and "large-r" not in model_name:
+ # large checkpoints take way too long to download
+ checkpoint_path = model_name_to_path[model_name]
+ state_dict = torch.load(checkpoint_path, map_location="cpu", weights_only=True)["model"]
+ else:
+ checkpoint_url = model_name_to_url[model_name]
+ state_dict = torch.hub.load_state_dict_from_url(checkpoint_url, map_location="cpu", file_name=model_name)[
+ "model"
+ ]
+ # rename keys
+ prefix = "module." if model_name == "git-base" else ""
+ rename_keys = create_rename_keys(config, prefix=prefix)
+ for src, dest in rename_keys:
+ rename_key(state_dict, src, dest)
+ read_in_q_k_v(state_dict, config, prefix=prefix)
+
+ # load HuggingFace model
+ model = GitForCausalLM(config)
+ missing_keys, unexpected_keys = model.load_state_dict(state_dict, strict=False)
+ model.eval()
+
+ print("Missing keys:", missing_keys)
+ print("Unexpected keys:", unexpected_keys)
+
+ assert missing_keys == ["git.embeddings.position_ids", "git.image_encoder.vision_model.embeddings.position_ids"]
+ assert unexpected_keys == ["git.image_encoder.visual_projection.weight"]
+
+ # verify results
+ image_processor = (
+ VideoMAEImageProcessor(
+ size={"shortest_edge": image_size}, crop_size={"height": image_size, "width": image_size}
+ )
+ if is_video
+ else CLIPImageProcessor(
+ size={"shortest_edge": image_size}, crop_size={"height": image_size, "width": image_size}
+ )
+ )
+ tokenizer = AutoTokenizer.from_pretrained(
+ "google-bert/bert-base-uncased", model_input_names=["input_ids", "attention_mask"]
+ )
+ processor = GitProcessor(tokenizer=tokenizer, image_processor=image_processor)
+
+ if is_video:
+ video = prepare_video()
+ pixel_values = processor(images=list(video), return_tensors="pt").pixel_values
+ else:
+ image = prepare_img(model_name)
+ image_transforms = Compose(
+ [
+ Resize(image_size, interpolation=Image.BICUBIC),
+ CenterCrop(image_size),
+ ToTensor(),
+ Normalize((0.48145466, 0.4578275, 0.40821073), (0.26862954, 0.26130258, 0.27577711)),
+ ]
+ )
+ original_pixel_values = image_transforms(image).unsqueeze(0)
+ pixel_values = processor(images=image, return_tensors="pt").pixel_values
+
+ assert torch.allclose(pixel_values, original_pixel_values)
+
+ input_ids = torch.tensor([[101]])
+ outputs = model(input_ids, pixel_values=pixel_values)
+ logits = outputs.logits
+ print("Logits:", logits[0, -1, :3])
+
+ if model_name == "git-base":
+ expected_slice_logits = torch.tensor([-1.2832, -1.2835, -1.2840])
+ elif model_name == "git-base-coco":
+ expected_slice_logits = torch.tensor([-0.9925, -0.9930, -0.9935])
+ elif model_name == "git-base-textcaps":
+ expected_slice_logits = torch.tensor([-1.2980, -1.2983, -1.2985])
+ elif model_name == "git-base-vqav2":
+ expected_slice_logits = torch.tensor([-0.8570, -0.8568, -0.8561])
+ elif model_name == "git-base-textvqa":
+ expected_slice_logits = torch.tensor([-1.4085, -1.4083, -1.4082])
+ elif model_name == "git-base-vatex":
+ expected_slice_logits = torch.tensor([-1.3451, -1.3447, -1.3447])
+ elif model_name == "git-base-msrvtt-qa":
+ expected_slice_logits = torch.tensor([-0.8554, -0.8550, -0.8540])
+ elif model_name == "git-large":
+ expected_slice_logits = torch.tensor([-1.1708, -1.1707, -1.1705])
+ elif model_name == "git-large-coco":
+ expected_slice_logits = torch.tensor([-1.0425, -1.0423, -1.0422])
+ elif model_name == "git-large-textcaps":
+ expected_slice_logits = torch.tensor([-1.2705, -1.2708, -1.2706])
+ elif model_name == "git-large-vqav2":
+ expected_slice_logits = torch.tensor([-0.7042, -0.7043, -0.7043])
+ elif model_name == "git-large-textvqa":
+ expected_slice_logits = torch.tensor([-0.8590, -0.8592, -0.8590])
+ elif model_name == "git-large-vatex":
+ expected_slice_logits = torch.tensor([-1.0113, -1.0114, -1.0113])
+ elif model_name == "git-large-msrvtt-qa":
+ expected_slice_logits = torch.tensor([0.0130, 0.0134, 0.0131])
+ elif model_name == "git-large-r":
+ expected_slice_logits = torch.tensor([-1.1283, -1.1285, -1.1286])
+ elif model_name == "git-large-r-coco":
+ expected_slice_logits = torch.tensor([-0.9641, -0.9641, -0.9641])
+ elif model_name == "git-large-r-textcaps":
+ expected_slice_logits = torch.tensor([-1.1121, -1.1120, -1.1124])
+
+ assert torch.allclose(logits[0, -1, :3], expected_slice_logits, atol=1e-4)
+ print("Looks ok!")
+
+ prompt = ""
+ if "textvqa" in model_name:
+ prompt = "what does the front of the bus say at the top?"
+ elif "msrvtt-qa" in model_name:
+ prompt = "what does the woman eat?"
+ elif "vqa" in model_name:
+ prompt = "what are the cats doing?"
+ input_ids = tokenizer(prompt, add_special_tokens=False).input_ids
+ input_ids = [processor.tokenizer.cls_token_id] + input_ids
+ input_ids = torch.tensor(input_ids).unsqueeze(0)
+ print("Generating caption...")
+ generated_ids = model.generate(pixel_values=pixel_values, input_ids=input_ids, max_length=50)
+ print("Generated caption:", processor.batch_decode(generated_ids, skip_special_tokens=True))
+
+ if pytorch_dump_folder_path is not None:
+ Path(pytorch_dump_folder_path).mkdir(exist_ok=True)
+ print(f"Saving model and processor of {model_name} to {pytorch_dump_folder_path}")
+ model.save_pretrained(pytorch_dump_folder_path)
+ processor.save_pretrained(pytorch_dump_folder_path)
+
+ if push_to_hub:
+ print(f"Pushing model and processor of {model_name} to the hub...")
+ model.push_to_hub(f"microsoft/{model_name}")
+ processor.push_to_hub(f"microsoft/{model_name}")
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ # Required parameters
+ parser.add_argument(
+ "--model_name",
+ default="git-base",
+ type=str,
+ help="Name of the model you'd like to convert.",
+ )
+ parser.add_argument(
+ "--pytorch_dump_folder_path",
+ default=None,
+ type=str,
+ help="Path to the output PyTorch model directory.",
+ )
+ parser.add_argument(
+ "--push_to_hub",
+ action="store_true",
+ help="Whether to push the model to the hub.",
+ )
+
+ args = parser.parse_args()
+ convert_git_checkpoint(args.model_name, args.pytorch_dump_folder_path, args.push_to_hub)
diff --git a/third_party/transformers/src/transformers/models/git/modeling_git.py b/third_party/transformers/src/transformers/models/git/modeling_git.py
new file mode 100644
index 0000000000000000000000000000000000000000..507aa4f0ad31eb5d8065ed67db66815e8dcf56d5
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/git/modeling_git.py
@@ -0,0 +1,1249 @@
+# Copyright 2022 Microsoft Research and The HuggingFace Inc. team.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""PyTorch GIT model."""
+
+import math
+from collections.abc import Callable
+from dataclasses import dataclass
+
+import torch
+from torch import nn
+
+from ... import initialization as init
+from ...activations import ACT2FN
+from ...cache_utils import Cache, DynamicCache
+from ...configuration_utils import PreTrainedConfig
+from ...generation import GenerationMixin
+from ...masking_utils import create_masks_for_generate
+from ...modeling_layers import GradientCheckpointingLayer
+from ...modeling_outputs import (
+ BaseModelOutput,
+ BaseModelOutputWithPast,
+ BaseModelOutputWithPooling,
+ CausalLMOutputWithPast,
+)
+from ...modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
+from ...processing_utils import Unpack
+from ...pytorch_utils import apply_chunking_to_forward
+from ...utils import (
+ ModelOutput,
+ TransformersKwargs,
+ auto_docstring,
+ logging,
+ torch_int,
+)
+from ...utils.deprecation import deprecate_kwarg
+from ...utils.generic import merge_with_config_defaults
+from ...utils.output_capturing import capture_outputs
+from .configuration_git import GitConfig, GitVisionConfig
+
+
+logger = logging.get_logger(__name__)
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ Base class for vision model's outputs that also contains image embeddings of the pooling of the last hidden states.
+ """
+)
+# Copied from transformers.models.clip.modeling_clip.CLIPVisionModelOutput with CLIP->Git
+class GitVisionModelOutput(ModelOutput):
+ r"""
+ image_embeds (`torch.FloatTensor` of shape `(batch_size, output_dim)` *optional* returned when model is initialized with `with_projection=True`):
+ The image embeddings obtained by applying the projection layer to the pooler_output.
+ """
+
+ image_embeds: torch.FloatTensor | None = None
+ last_hidden_state: torch.FloatTensor | None = None
+ hidden_states: tuple[torch.FloatTensor, ...] | None = None
+ attentions: tuple[torch.FloatTensor, ...] | None = None
+
+
+# Copied from transformers.models.gemma3.modeling_gemma3.token_type_ids_mask_function
+def token_type_ids_mask_function(group_ids: torch.Tensor) -> Callable:
+ """
+ This function adds the correct offsets to the `q_idx` and `kv_idx` as the torch API can only accept lengths,
+ not start and end indices.
+ Args:
+ group_ids (`torch.Tensor`):
+ A tensor of shape `(bs, len)` assigning each token to a vision group. Tokens with the same group
+ come from the same input image. Text is denoted by `-1`.
+ """
+
+ def inner_mask(batch_idx: int, head_idx: int, q_idx: int, kv_idx: int) -> bool:
+ seq_length = group_ids.shape[-1]
+
+ # clamp indices because with static cache they can go beyond `group_ids.shape[-1]`
+ q_idx_clamped = q_idx.clamp(max=seq_length - 1)
+ kv_idx_clamped = kv_idx.clamp(max=seq_length - 1)
+
+ # Unmask if the q and kv come from same group which is not -1 (i.e. non-text)
+ q_group = group_ids[batch_idx, q_idx_clamped]
+ kv_group = group_ids[batch_idx, kv_idx_clamped]
+ q_group = torch.where(q_idx < seq_length, q_group, -1)
+ kv_group = torch.where(kv_idx < seq_length, kv_group, -1)
+ return (q_group == kv_group) & (q_group >= 0)
+
+ return inner_mask
+
+
+@deprecate_kwarg("input_embeds", version="5.6.0", new_name="inputs_embeds")
+# Copied from transformers.models.gemma3.modeling_gemma3.create_causal_mask_mapping
+def create_causal_mask_mapping(
+ config: PreTrainedConfig,
+ inputs_embeds: torch.Tensor,
+ attention_mask: torch.Tensor | None,
+ past_key_values: Cache | None,
+ position_ids: torch.Tensor | None,
+ token_type_ids: torch.Tensor | None = None,
+ pixel_values: torch.FloatTensor | None = None,
+ is_training: bool = False,
+ is_first_iteration: bool | None = None,
+ **kwargs,
+) -> dict:
+ """
+ Overwrites the base `create_masks_for_generate` with `token_type_ids` masking to create the causal mask mapping
+ for all kinds of forward passes. Gemma3 uses a bidirectional mask for images.
+
+ Uses `pixel_values` as an optional input to disambiguate edge cases.
+ """
+ if is_training and token_type_ids is None:
+ raise ValueError("`token_type_ids` is required as a model input when training")
+
+ mask_kwargs = {
+ "config": config.get_text_config(),
+ "inputs_embeds": inputs_embeds,
+ "attention_mask": attention_mask,
+ "past_key_values": past_key_values,
+ "position_ids": position_ids,
+ }
+ # NOTE: this `may_have_image_input` logic is not flawless, it fails when we're using a cache eagerly initialized
+ # (e.g. compiled prefill) AND `pixel_values` are not provided (i.e. the image data is provided through other
+ # means). Determining prefill in that case requires checking data values, which is not compile-compatible.
+ is_first_iteration = (
+ is_first_iteration
+ if is_first_iteration is not None
+ else (past_key_values is None or not past_key_values.is_initialized or pixel_values is not None)
+ )
+ if token_type_ids is not None and is_first_iteration:
+ # We need to pass an additional mask function to account for token type ids, and it needs to be an `or` (to
+ # undo the causal masking)
+
+ # First find where a new image block starts: 1 if image and previous not image
+ # The images cannot attend to future images, but can attend to all prev images and to itself bidirectionally
+ is_image = (token_type_ids == 1).to(inputs_embeds.device)
+ is_previous_image = nn.functional.pad(is_image, (1, 0), value=0)[:, :-1]
+ new_image_start = is_image & ~is_previous_image
+ group_ids = torch.cumsum(new_image_start.int(), dim=1) - 1
+ group_ids = torch.where(is_image, group_ids, -1)
+ mask_kwargs["or_mask_function"] = token_type_ids_mask_function(group_ids)
+
+ return create_masks_for_generate(**mask_kwargs)
+
+
+class GitEmbeddings(nn.Module):
+ """Construct the embeddings from word and position embeddings."""
+
+ def __init__(self, config):
+ super().__init__()
+ self.word_embeddings = nn.Embedding(config.vocab_size, config.hidden_size, padding_idx=config.pad_token_id)
+ self.position_embeddings = nn.Embedding(config.max_position_embeddings, config.hidden_size)
+
+ self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
+ self.dropout = nn.Dropout(config.hidden_dropout_prob)
+ # position_ids (1, len position emb) is contiguous in memory and exported when serialized
+ self.register_buffer(
+ "position_ids", torch.arange(config.max_position_embeddings).expand((1, -1)), persistent=False
+ )
+
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ past_key_values_length: int = 0,
+ ) -> torch.Tensor:
+ if input_ids is not None:
+ input_shape = input_ids.size()
+ else:
+ input_shape = inputs_embeds.size()[:-1]
+
+ seq_length = input_shape[1]
+
+ if position_ids is None:
+ position_ids = self.position_ids[:, past_key_values_length : seq_length + past_key_values_length]
+
+ if inputs_embeds is None:
+ embeddings = self.word_embeddings(input_ids)
+ else:
+ embeddings = inputs_embeds
+
+ position_embeddings = self.position_embeddings(position_ids)
+ embeddings += position_embeddings
+
+ embeddings = self.LayerNorm(embeddings)
+ embeddings = self.dropout(embeddings)
+ return embeddings
+
+
+class GitSelfAttention(nn.Module):
+ def __init__(self, config, layer_idx=None):
+ super().__init__()
+ if config.hidden_size % config.num_attention_heads != 0 and not hasattr(config, "embedding_size"):
+ raise ValueError(
+ f"The hidden size ({config.hidden_size}) is not a multiple of the number of attention "
+ f"heads ({config.num_attention_heads})"
+ )
+ self.layer_idx = layer_idx
+ if layer_idx is None:
+ logger.warning_once(
+ f"Instantiating {self.__class__.__name__} without passing a `layer_idx` is not recommended and will "
+ "lead to errors during the forward call if caching is used. Please make sure to provide a `layer_idx` "
+ "when creating this class."
+ )
+
+ self.num_attention_heads = config.num_attention_heads
+ self.attention_head_size = int(config.hidden_size / config.num_attention_heads)
+ self.all_head_size = self.num_attention_heads * self.attention_head_size
+ self.image_patch_tokens = int((config.vision_config.image_size / config.vision_config.patch_size) ** 2 + 1)
+ if config.num_image_with_embedding is not None:
+ self.image_patch_tokens *= config.num_image_with_embedding
+
+ self.query = nn.Linear(config.hidden_size, self.all_head_size)
+ self.key = nn.Linear(config.hidden_size, self.all_head_size)
+ self.value = nn.Linear(config.hidden_size, self.all_head_size)
+
+ self.dropout = nn.Dropout(config.attention_probs_dropout_prob)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ attention_mask: torch.FloatTensor | None = None,
+ past_key_values: Cache | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple[torch.Tensor]:
+ batch_size = hidden_states.shape[0]
+ query_layer = (
+ self.query(hidden_states)
+ .view(batch_size, -1, self.num_attention_heads, self.attention_head_size)
+ .transpose(1, 2)
+ )
+
+ key_layer = (
+ self.key(hidden_states)
+ .view(batch_size, -1, self.num_attention_heads, self.attention_head_size)
+ .transpose(1, 2)
+ )
+ value_layer = (
+ self.value(hidden_states)
+ .view(batch_size, -1, self.num_attention_heads, self.attention_head_size)
+ .transpose(1, 2)
+ )
+ if past_key_values is not None:
+ key_layer, value_layer = past_key_values.update(key_layer, value_layer, self.layer_idx)
+
+ # Take the dot product between "query" and "key" to get the raw attention scores.
+ attention_scores = torch.matmul(query_layer, key_layer.transpose(-1, -2))
+
+ attention_scores = attention_scores / math.sqrt(self.attention_head_size)
+ if attention_mask is not None:
+ # Apply the attention mask is (precomputed for all layers in GitModel forward() function)
+ attention_scores = attention_scores + attention_mask
+
+ # Normalize the attention scores to probabilities.
+ attention_probs = nn.functional.softmax(attention_scores, dim=-1)
+
+ # This is actually dropping out entire tokens to attend to, which might
+ # seem a bit unusual, but is taken from the original Transformer paper.
+ attention_probs = self.dropout(attention_probs)
+
+ context_layer = torch.matmul(attention_probs, value_layer)
+
+ context_layer = context_layer.permute(0, 2, 1, 3).contiguous()
+ new_context_layer_shape = context_layer.size()[:-2] + (self.all_head_size,)
+ context_layer = context_layer.view(new_context_layer_shape)
+
+ return context_layer, attention_probs
+
+
+# Copied from transformers.models.bert.modeling_bert.BertSelfOutput
+class GitSelfOutput(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+ self.dense = nn.Linear(config.hidden_size, config.hidden_size)
+ self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
+ self.dropout = nn.Dropout(config.hidden_dropout_prob)
+
+ def forward(self, hidden_states: torch.Tensor, input_tensor: torch.Tensor) -> torch.Tensor:
+ hidden_states = self.dense(hidden_states)
+ hidden_states = self.dropout(hidden_states)
+ hidden_states = self.LayerNorm(hidden_states + input_tensor)
+ return hidden_states
+
+
+GIT_SELF_ATTENTION_CLASSES = {
+ "eager": GitSelfAttention,
+}
+
+
+class GitAttention(nn.Module):
+ def __init__(self, config, layer_idx=None):
+ super().__init__()
+ self.self = GIT_SELF_ATTENTION_CLASSES[config._attn_implementation](config, layer_idx=layer_idx)
+ self.output = GitSelfOutput(config)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ attention_mask: torch.FloatTensor | None = None,
+ past_key_values: Cache | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple[torch.Tensor]:
+ attn_output, _ = self.self(
+ hidden_states,
+ attention_mask,
+ past_key_values,
+ **kwargs,
+ )
+ attention_output = self.output(attn_output, hidden_states)
+ return attention_output
+
+
+# Copied from transformers.models.bert.modeling_bert.BertIntermediate
+class GitIntermediate(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+ self.dense = nn.Linear(config.hidden_size, config.intermediate_size)
+ if isinstance(config.hidden_act, str):
+ self.intermediate_act_fn = ACT2FN[config.hidden_act]
+ else:
+ self.intermediate_act_fn = config.hidden_act
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ hidden_states = self.dense(hidden_states)
+ hidden_states = self.intermediate_act_fn(hidden_states)
+ return hidden_states
+
+
+# Copied from transformers.models.bert.modeling_bert.BertOutput
+class GitOutput(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+ self.dense = nn.Linear(config.intermediate_size, config.hidden_size)
+ self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
+ self.dropout = nn.Dropout(config.hidden_dropout_prob)
+
+ def forward(self, hidden_states: torch.Tensor, input_tensor: torch.Tensor) -> torch.Tensor:
+ hidden_states = self.dense(hidden_states)
+ hidden_states = self.dropout(hidden_states)
+ hidden_states = self.LayerNorm(hidden_states + input_tensor)
+ return hidden_states
+
+
+class GitLayer(GradientCheckpointingLayer):
+ def __init__(self, config, layer_idx=None):
+ super().__init__()
+ self.chunk_size_feed_forward = config.chunk_size_feed_forward
+ self.seq_len_dim = 1
+ self.attention = GitAttention(config, layer_idx=layer_idx)
+ self.intermediate = GitIntermediate(config)
+ self.output = GitOutput(config)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ attention_mask: torch.FloatTensor | None = None,
+ past_key_values: Cache | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple[torch.Tensor]:
+ attention_output = self.attention(
+ hidden_states,
+ attention_mask,
+ past_key_values=past_key_values,
+ **kwargs,
+ )
+
+ layer_output = apply_chunking_to_forward(
+ self.feed_forward_chunk, self.chunk_size_feed_forward, self.seq_len_dim, attention_output
+ )
+ return layer_output
+
+ def feed_forward_chunk(self, attention_output):
+ intermediate_output = self.intermediate(attention_output)
+ layer_output = self.output(intermediate_output, attention_output)
+ return layer_output
+
+
+class GitEncoder(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+ self.config = config
+ self.layer = nn.ModuleList([GitLayer(config, i) for i in range(config.num_hidden_layers)])
+ self.gradient_checkpointing = False
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ attention_mask: torch.FloatTensor | None = None,
+ past_key_values: Cache | None = None,
+ use_cache: bool | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> BaseModelOutputWithPast:
+ for layer_module in self.layer:
+ hidden_states = layer_module(
+ hidden_states,
+ attention_mask,
+ past_key_values,
+ **kwargs,
+ )
+
+ return BaseModelOutputWithPast(
+ last_hidden_state=hidden_states,
+ past_key_values=past_key_values,
+ )
+
+
+@auto_docstring
+class GitPreTrainedModel(PreTrainedModel):
+ config: GitConfig
+ base_model_prefix = "git"
+ input_modalities = ("image", "text")
+ supports_gradient_checkpointing = True
+
+ @torch.no_grad()
+ def _init_weights(self, module):
+ """Initialize the weights"""
+ if isinstance(module, GitVisionEmbeddings):
+ init.normal_(module.class_embedding, mean=0.0, std=self.config.initializer_range)
+ init.normal_(module.patch_embedding.weight, std=self.config.initializer_range)
+ init.normal_(module.position_embedding.weight, std=self.config.initializer_range)
+ init.copy_(module.position_ids, torch.arange(module.position_ids.shape[-1]).expand((1, -1)))
+ if isinstance(module, nn.Linear):
+ init.normal_(module.weight, mean=0.0, std=self.config.initializer_range)
+ if module.bias is not None:
+ init.zeros_(module.bias)
+ elif isinstance(module, nn.Embedding):
+ init.normal_(module.weight, mean=0.0, std=self.config.initializer_range)
+ # Here we need the check explicitly, as we slice the weight in the `zeros_` call, so it looses the flag
+ if module.padding_idx is not None and not getattr(module.weight, "_is_hf_initialized", False):
+ init.zeros_(module.weight[module.padding_idx])
+ elif isinstance(module, nn.LayerNorm):
+ init.zeros_(module.bias)
+ init.ones_(module.weight)
+ elif isinstance(module, GitEmbeddings):
+ init.copy_(module.position_ids, torch.arange(module.position_ids.shape[-1]).expand((1, -1)))
+
+
+# Copied from transformers.models.clip.modeling_clip.CLIPVisionEmbeddings with CLIP->Git
+class GitVisionEmbeddings(nn.Module):
+ def __init__(self, config: GitVisionConfig):
+ super().__init__()
+ self.config = config
+ self.embed_dim = config.hidden_size
+ self.image_size = config.image_size
+ self.patch_size = config.patch_size
+
+ self.class_embedding = nn.Parameter(torch.randn(self.embed_dim))
+
+ self.patch_embedding = nn.Conv2d(
+ in_channels=config.num_channels,
+ out_channels=self.embed_dim,
+ kernel_size=self.patch_size,
+ stride=self.patch_size,
+ bias=False,
+ )
+
+ self.num_patches = (self.image_size // self.patch_size) ** 2
+ self.num_positions = self.num_patches + 1
+ self.position_embedding = nn.Embedding(self.num_positions, self.embed_dim)
+ self.register_buffer("position_ids", torch.arange(self.num_positions).expand((1, -1)), persistent=False)
+
+ def interpolate_pos_encoding(self, embeddings: torch.Tensor, height: int, width: int) -> torch.Tensor:
+ """
+ This method allows to interpolate the pre-trained position encodings, to be able to use the model on higher resolution
+ images. This method is also adapted to support torch.jit tracing.
+
+ Adapted from:
+ - https://github.com/facebookresearch/dino/blob/de9ee3df6cf39fac952ab558447af1fa1365362a/vision_transformer.py#L174-L194, and
+ - https://github.com/facebookresearch/dinov2/blob/e1277af2ba9496fbadf7aec6eba56e8d882d1e35/dinov2/models/vision_transformer.py#L179-L211
+ """
+
+ num_patches = embeddings.shape[1] - 1
+ position_embedding = self.position_embedding.weight.unsqueeze(0)
+ num_positions = position_embedding.shape[1] - 1
+
+ # always interpolate when tracing to ensure the exported model works for dynamic input shapes
+ if not torch.jit.is_tracing() and num_patches == num_positions and height == width:
+ return self.position_embedding(self.position_ids)
+
+ class_pos_embed = position_embedding[:, :1]
+ patch_pos_embed = position_embedding[:, 1:]
+
+ dim = embeddings.shape[-1]
+
+ new_height = height // self.patch_size
+ new_width = width // self.patch_size
+
+ sqrt_num_positions = torch_int(num_positions**0.5)
+ patch_pos_embed = patch_pos_embed.reshape(1, sqrt_num_positions, sqrt_num_positions, dim)
+ patch_pos_embed = patch_pos_embed.permute(0, 3, 1, 2)
+
+ patch_pos_embed = nn.functional.interpolate(
+ patch_pos_embed,
+ size=(new_height, new_width),
+ mode="bicubic",
+ align_corners=False,
+ )
+
+ patch_pos_embed = patch_pos_embed.permute(0, 2, 3, 1).view(1, -1, dim)
+
+ return torch.cat((class_pos_embed, patch_pos_embed), dim=1)
+
+ def forward(self, pixel_values: torch.FloatTensor, interpolate_pos_encoding=False) -> torch.Tensor:
+ batch_size, _, height, width = pixel_values.shape
+ if not interpolate_pos_encoding and (height != self.image_size or width != self.image_size):
+ raise ValueError(
+ f"Input image size ({height}*{width}) doesn't match model ({self.image_size}*{self.image_size})."
+ )
+ target_dtype = self.patch_embedding.weight.dtype
+ patch_embeds = self.patch_embedding(pixel_values.to(dtype=target_dtype)) # shape = [*, width, grid, grid]
+ patch_embeds = patch_embeds.flatten(2).transpose(1, 2)
+
+ class_embeds = self.class_embedding.expand(batch_size, 1, -1)
+ embeddings = torch.cat([class_embeds, patch_embeds], dim=1)
+ if interpolate_pos_encoding:
+ embeddings = embeddings + self.interpolate_pos_encoding(embeddings, height, width)
+ else:
+ embeddings = embeddings + self.position_embedding(self.position_ids)
+ return embeddings
+
+
+class GitVisionMLP(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+ self.config = config
+ self.activation_fn = ACT2FN[config.hidden_act]
+ self.fc1 = nn.Linear(config.hidden_size, config.intermediate_size)
+ self.fc2 = nn.Linear(config.intermediate_size, config.hidden_size)
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ hidden_states = self.fc1(hidden_states)
+ hidden_states = self.activation_fn(hidden_states)
+ hidden_states = self.fc2(hidden_states)
+ return hidden_states
+
+
+# Copied from transformers.models.siglip.modeling_siglip.eager_attention_forward
+def eager_attention_forward(
+ module: nn.Module,
+ query: torch.Tensor,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ attention_mask: torch.Tensor | None,
+ scaling: float,
+ dropout: float = 0.0,
+ **kwargs,
+):
+ attn_weights = torch.matmul(query, key.transpose(-1, -2)) * scaling
+ if attention_mask is not None:
+ attn_weights = attn_weights + attention_mask
+
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query.dtype)
+ attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
+
+ attn_output = torch.matmul(attn_weights, value)
+ attn_output = attn_output.transpose(1, 2).contiguous()
+
+ return attn_output, attn_weights
+
+
+class GitVisionAttention(nn.Module):
+ """Multi-headed attention from 'Attention Is All You Need' paper"""
+
+ def __init__(self, config):
+ super().__init__()
+ self.config = config
+ self.embed_dim = config.hidden_size
+ self.num_heads = config.num_attention_heads
+ self.head_dim = self.embed_dim // self.num_heads
+ if self.head_dim * self.num_heads != self.embed_dim:
+ raise ValueError(
+ f"embed_dim must be divisible by num_heads (got `embed_dim`: {self.embed_dim} and `num_heads`:"
+ f" {self.num_heads})."
+ )
+ self.scale = self.head_dim**-0.5
+ self.dropout = config.attention_dropout
+ self.is_causal = False
+
+ self.k_proj = nn.Linear(self.embed_dim, self.embed_dim)
+ self.v_proj = nn.Linear(self.embed_dim, self.embed_dim)
+ self.q_proj = nn.Linear(self.embed_dim, self.embed_dim)
+ self.out_proj = nn.Linear(self.embed_dim, self.embed_dim)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ attention_mask: torch.Tensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
+ """Input shape: Batch x Time x Channel"""
+
+ batch_size, seq_length, embed_dim = hidden_states.shape
+
+ queries = self.q_proj(hidden_states)
+ keys = self.k_proj(hidden_states)
+ values = self.v_proj(hidden_states)
+
+ queries = queries.view(batch_size, seq_length, self.num_heads, self.head_dim).transpose(1, 2)
+ keys = keys.view(batch_size, seq_length, self.num_heads, self.head_dim).transpose(1, 2)
+ values = values.view(batch_size, seq_length, self.num_heads, self.head_dim).transpose(1, 2)
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ queries,
+ keys,
+ values,
+ attention_mask,
+ is_causal=self.is_causal,
+ scaling=self.scale,
+ dropout=0.0 if not self.training else self.dropout,
+ **kwargs,
+ )
+
+ attn_output = attn_output.reshape(batch_size, seq_length, embed_dim).contiguous()
+ attn_output = self.out_proj(attn_output)
+ return attn_output, attn_weights
+
+
+# Copied from transformers.models.altclip.modeling_altclip.AltCLIPEncoderLayer with AltCLIP->GitVision
+class GitVisionEncoderLayer(GradientCheckpointingLayer):
+ def __init__(self, config: GitVisionConfig):
+ super().__init__()
+ self.embed_dim = config.hidden_size
+ self.self_attn = GitVisionAttention(config)
+ self.layer_norm1 = nn.LayerNorm(self.embed_dim, eps=config.layer_norm_eps)
+ self.mlp = GitVisionMLP(config)
+ self.layer_norm2 = nn.LayerNorm(self.embed_dim, eps=config.layer_norm_eps)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ attention_mask: torch.Tensor,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple[torch.FloatTensor, torch.Tensor | None]:
+ residual = hidden_states
+
+ hidden_states = self.layer_norm1(hidden_states)
+ hidden_states, _ = self.self_attn(
+ hidden_states=hidden_states,
+ attention_mask=attention_mask,
+ **kwargs,
+ )
+ hidden_states = residual + hidden_states
+
+ residual = hidden_states
+ hidden_states = self.layer_norm2(hidden_states)
+ hidden_states = self.mlp(hidden_states)
+ hidden_states = residual + hidden_states
+
+ return hidden_states
+
+
+# Copied from transformers.models.altclip.modeling_altclip.AltCLIPEncoder with AltCLIP->GitVision, CLIPConfig
+class GitVisionEncoder(nn.Module):
+ """
+ Transformer encoder consisting of `config.num_hidden_layers` self attention layers. Each layer is a
+ [`GitVisionEncoderLayer`].
+
+ Args:
+ config: GitVisionConfig
+ """
+
+ def __init__(self, config: GitVisionConfig):
+ super().__init__()
+ self.config = config
+ self.layers = nn.ModuleList([GitVisionEncoderLayer(config) for _ in range(config.num_hidden_layers)])
+ self.gradient_checkpointing = False
+
+ def forward(
+ self,
+ inputs_embeds,
+ attention_mask: torch.Tensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | BaseModelOutput:
+ r"""
+ Args:
+ inputs_embeds (`torch.FloatTensor` of shape `(batch_size, sequence_length, hidden_size)`):
+ Optionally, instead of passing `input_ids` you can choose to directly pass an embedded representation.
+ This is useful if you want more control over how to convert `input_ids` indices into associated vectors
+ than the model's internal embedding lookup matrix.
+ attention_mask (`torch.Tensor` of shape `(batch_size, sequence_length)`, *optional*):
+ Mask to avoid performing attention on padding token indices. Mask values selected in `[0, 1]`:
+
+ - 1 for tokens that are **not masked**,
+ - 0 for tokens that are **masked**.
+
+ [What are attention masks?](../glossary#attention-mask)
+
+ """
+ hidden_states = inputs_embeds
+ for encoder_layer in self.layers:
+ hidden_states = encoder_layer(
+ hidden_states,
+ attention_mask,
+ **kwargs,
+ )
+
+ return BaseModelOutput(
+ last_hidden_state=hidden_states,
+ )
+
+
+class GitVisionTransformer(nn.Module):
+ # Copied from transformers.models.altclip.modeling_altclip.AltCLIPVisionTransformer.__init__ with AltCLIPEncoder->GitVisionEncoder, AltCLIP->Git
+ def __init__(self, config: GitVisionConfig):
+ super().__init__()
+ self.config = config
+ embed_dim = config.hidden_size
+
+ self.embeddings = GitVisionEmbeddings(config)
+ self.pre_layrnorm = nn.LayerNorm(embed_dim, eps=config.layer_norm_eps)
+ self.encoder = GitVisionEncoder(config)
+ self.post_layernorm = nn.LayerNorm(embed_dim, eps=config.layer_norm_eps)
+
+ @auto_docstring
+ def forward(
+ self,
+ pixel_values: torch.FloatTensor | None = None,
+ interpolate_pos_encoding: bool | None = False,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> BaseModelOutput:
+ if pixel_values is None:
+ raise ValueError("You have to specify pixel_values")
+
+ hidden_states = self.embeddings(pixel_values, interpolate_pos_encoding=interpolate_pos_encoding)
+ hidden_states = self.pre_layrnorm(hidden_states)
+
+ encoder_outputs = self.encoder(
+ inputs_embeds=hidden_states,
+ **kwargs,
+ )
+
+ last_hidden_state = encoder_outputs.last_hidden_state
+
+ last_hidden_state = self.post_layernorm(last_hidden_state)
+
+ return BaseModelOutput(
+ last_hidden_state=last_hidden_state,
+ )
+
+
+@auto_docstring(
+ custom_intro="""
+ The vision model from CLIP, used in GIT, without any head or projection on top.
+ """
+)
+class GitVisionModel(GitPreTrainedModel):
+ config: GitVisionConfig
+ main_input_name = "pixel_values"
+ input_modalities = ("image",)
+ _can_record_outputs = {
+ "hidden_states": GitVisionEncoderLayer,
+ "attentions": GitVisionAttention,
+ }
+
+ # Copied from transformers.models.clip.modeling_clip.CLIPVisionModel.__init__ with CLIP->Git
+ def __init__(self, config: GitVisionConfig):
+ super().__init__(config)
+ self.vision_model = GitVisionTransformer(config)
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ def get_input_embeddings(self) -> nn.Module:
+ return self.vision_model.embeddings.patch_embedding
+
+ @merge_with_config_defaults
+ @capture_outputs(tie_last_hidden_states=False)
+ @auto_docstring
+ def forward(
+ self,
+ pixel_values: torch.FloatTensor | None = None,
+ interpolate_pos_encoding: bool = False,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | BaseModelOutput:
+ r"""
+ Examples:
+
+ ```python
+ >>> from PIL import Image
+ >>> import httpx
+ >>> from io import BytesIO
+ >>> from transformers import AutoProcessor, GitVisionModel
+
+ >>> processor = AutoProcessor.from_pretrained("microsoft/git-base")
+ >>> model = GitVisionModel.from_pretrained("microsoft/git-base")
+
+ >>> url = "http://images.cocodataset.org/val2017/000000039769.jpg"
+ >>> with httpx.stream("GET", url) as response:
+ ... image = Image.open(BytesIO(response.read()))
+
+ >>> inputs = processor(images=image, return_tensors="pt")
+
+ >>> outputs = model(**inputs)
+ >>> last_hidden_state = outputs.last_hidden_state
+ ```"""
+ return self.vision_model(
+ pixel_values=pixel_values,
+ interpolate_pos_encoding=interpolate_pos_encoding,
+ **kwargs,
+ )
+
+
+class GitProjection(nn.Module):
+ def __init__(self, config: GitConfig):
+ super().__init__()
+ self.config = config
+ self.visual_projection = nn.Sequential(
+ nn.Linear(config.vision_config.hidden_size, config.hidden_size),
+ nn.LayerNorm(config.hidden_size, eps=config.vision_config.layer_norm_eps),
+ )
+
+ def forward(self, embeddings: torch.Tensor) -> torch.Tensor:
+ return self.visual_projection(embeddings)
+
+
+@auto_docstring(
+ custom_intro="""
+ The bare GIT Model transformer consisting of a CLIP image encoder and text decoder outputting raw hidden-states
+ """
+)
+class GitModel(GitPreTrainedModel):
+ _can_record_outputs = {
+ "hidden_states": GitLayer,
+ "attentions": GitSelfAttention,
+ }
+
+ def __init__(self, config):
+ super().__init__(config)
+ self.config = config
+
+ self.embeddings = GitEmbeddings(config)
+ self.image_encoder = GitVisionModel(config.vision_config)
+ self.encoder = GitEncoder(config)
+
+ self.visual_projection = GitProjection(config)
+
+ if config.num_image_with_embedding is not None:
+ self.img_temporal_embedding = nn.ParameterList(
+ nn.Parameter(torch.zeros(1, 1, config.vision_config.hidden_size))
+ for _ in range(config.num_image_with_embedding)
+ )
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ def get_input_embeddings(self):
+ return self.embeddings.word_embeddings
+
+ def set_input_embeddings(self, value):
+ self.embeddings.word_embeddings = value
+
+ @merge_with_config_defaults
+ @capture_outputs
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.Tensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.Tensor | None = None,
+ pixel_values: torch.Tensor | None = None,
+ inputs_embeds: torch.Tensor | None = None,
+ past_key_values: Cache | None = None,
+ use_cache: bool | None = None,
+ interpolate_pos_encoding: bool = False,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple[torch.Tensor] | BaseModelOutputWithPooling:
+ r"""
+ Examples:
+
+ ```python
+ >>> from transformers import AutoProcessor, AutoModel
+ >>> import httpx
+ >>> from io import BytesIO
+ >>> from PIL import Image
+
+ >>> processor = AutoProcessor.from_pretrained("microsoft/git-base")
+ >>> model = AutoModel.from_pretrained("microsoft/git-base")
+
+ >>> url = "http://images.cocodataset.org/val2017/000000039769.jpg"
+ >>> with httpx.stream("GET", url) as response:
+ ... image = Image.open(BytesIO(response.read()))
+
+ >>> text = "this is an image of two cats"
+
+ >>> inputs = processor(images=image, text=text, return_tensors="pt")
+
+ >>> outputs = model(**inputs)
+ >>> last_hidden_state = outputs.last_hidden_state
+ ```"""
+ if (input_ids is None) ^ (inputs_embeds is not None):
+ raise ValueError("You must specify exactly one of input_ids or inputs_embeds")
+
+ if use_cache and past_key_values is None:
+ past_key_values = DynamicCache(config=self.config)
+
+ # past_key_values_length
+ past_key_values_length = 0
+ if past_key_values is not None:
+ past_key_values_length = (
+ past_key_values.get_seq_length()
+ if not isinstance(past_key_values, Cache)
+ else past_key_values.get_seq_length()
+ )
+
+ # Adjust position ids by adding image seq length
+ if pixel_values is None and past_key_values is not None and input_ids.shape[1] == 1:
+ position_ids = position_ids + past_key_values_length
+
+ embedding_output = self.embeddings(
+ input_ids=input_ids,
+ position_ids=position_ids,
+ inputs_embeds=inputs_embeds,
+ past_key_values_length=past_key_values_length,
+ )
+
+ # Always create `token_type_ids` so we can re-use Gemma3 style mask preparation fn
+ token_type_ids = torch.zeros_like(embedding_output, dtype=torch.int)[..., 0]
+
+ if pixel_values is not None:
+ if pixel_values.ndim == 4:
+ # here we assume pixel_values is of shape (batch_size, num_channels, height, width)
+ visual_features = self.image_encoder(
+ pixel_values, interpolate_pos_encoding=interpolate_pos_encoding
+ ).last_hidden_state
+
+ elif pixel_values.ndim == 5:
+ # here we assume pixel_values is of shape (batch_size, num_frames, num_channels, height, width)
+ visual_features = []
+ for frame_idx in range(pixel_values.shape[1]):
+ visual_features_frame = self.image_encoder(
+ pixel_values[:, frame_idx, :, :], interpolate_pos_encoding=interpolate_pos_encoding
+ ).last_hidden_state
+ visual_features_frame += self.img_temporal_embedding[frame_idx]
+ visual_features.append(visual_features_frame)
+
+ # finally, concatenate all features along sequence dimension
+ visual_features = torch.cat(visual_features, dim=1)
+
+ else:
+ raise ValueError("pixel_values must be of rank 4 or 5")
+
+ projected_visual_features = self.visual_projection(visual_features)
+
+ # Repeat visual features to match embedding batch size.
+ projected_visual_features = projected_visual_features.repeat(
+ embedding_output.size(0) // projected_visual_features.size(0), 1, 1
+ )
+
+ # concatenate patch token and text token embeddings
+ embedding_output = torch.cat((projected_visual_features, embedding_output), dim=1)
+ image_token_type_ids = torch.ones_like(projected_visual_features, dtype=torch.int)[..., 0]
+ token_type_ids = torch.cat([image_token_type_ids, token_type_ids], dim=-1)
+ if attention_mask is not None:
+ attention_mask = torch.cat([torch.ones_like(image_token_type_ids), attention_mask], dim=-1)
+ elif past_key_values is not None and input_ids.shape[1] == 1:
+ # Expand attention mask and cache position with image tokens because GIT doesn't add image
+ # placeholder tokens when processing. Doesn't worth the refactor, low usage!
+ extended_attention_mask = torch.ones(
+ (attention_mask.shape[0], past_key_values_length - attention_mask.shape[1] + 1),
+ dtype=attention_mask.dtype,
+ device=attention_mask.device,
+ )
+ attention_mask = torch.cat([extended_attention_mask, attention_mask], dim=-1)
+
+ # Images attend each other bidirectionally while text remains causal
+ causal_mask = create_causal_mask_mapping(
+ self.config,
+ embedding_output,
+ attention_mask,
+ past_key_values,
+ None,
+ token_type_ids,
+ pixel_values,
+ )
+
+ hidden_states = embedding_output
+
+ encoder_outputs: BaseModelOutputWithPast = self.encoder(
+ hidden_states,
+ attention_mask=causal_mask,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ return BaseModelOutputWithPast(
+ last_hidden_state=encoder_outputs.last_hidden_state,
+ past_key_values=encoder_outputs.past_key_values,
+ )
+
+
+@auto_docstring(
+ custom_intro="""
+ GIT Model with a `language modeling` head on top for autoregressive language modeling.
+ """
+)
+class GitForCausalLM(GitPreTrainedModel, GenerationMixin):
+ _tied_weights_keys = {"output.weight": "git.embeddings.word_embeddings.weight"}
+
+ def __init__(self, config):
+ super().__init__(config)
+
+ self.git = GitModel(config)
+ self.output = nn.Linear(config.hidden_size, config.vocab_size)
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ def get_output_embeddings(self):
+ return self.output
+
+ def set_output_embeddings(self, new_embeddings):
+ self.output = new_embeddings
+
+ @merge_with_config_defaults
+ @capture_outputs
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.Tensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.Tensor | None = None,
+ pixel_values: torch.Tensor | None = None,
+ inputs_embeds: torch.Tensor | None = None,
+ labels: torch.Tensor | None = None,
+ past_key_values: Cache | None = None,
+ use_cache: bool | None = None,
+ interpolate_pos_encoding: bool = False,
+ logits_to_keep: int | torch.Tensor = 0,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple[torch.Tensor] | CausalLMOutputWithPast:
+ r"""
+ labels (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*):
+ Labels for computing the left-to-right language modeling loss (next word prediction). Indices should be in
+ `[-100, 0, ..., config.vocab_size]` (see `input_ids` docstring) Tokens with indices set to `-100` are
+ ignored (masked), the loss is only computed for the tokens with labels n `[0, ..., config.vocab_size]`
+
+ Examples:
+
+ Image captioning example:
+
+ ```python
+ >>> from transformers import AutoProcessor, AutoModelForCausalLM
+ >>> import httpx
+ >>> from io import BytesIO
+ >>> from PIL import Image
+
+ >>> processor = AutoProcessor.from_pretrained("microsoft/git-base-coco")
+ >>> model = AutoModelForCausalLM.from_pretrained("microsoft/git-base-coco")
+
+ >>> url = "http://images.cocodataset.org/val2017/000000039769.jpg"
+ >>> with httpx.stream("GET", url) as response:
+ ... image = Image.open(BytesIO(response.read()))
+
+ >>> pixel_values = processor(images=image, return_tensors="pt").pixel_values
+
+ >>> generated_ids = model.generate(pixel_values=pixel_values, max_length=50)
+ >>> generated_caption = processor.batch_decode(generated_ids, skip_special_tokens=True)[0]
+ >>> print(generated_caption)
+ two cats sleeping on a pink blanket next to remotes.
+ ```
+
+ Visual question answering (VQA) example:
+
+ ```python
+ >>> from transformers import AutoProcessor, AutoModelForCausalLM
+ >>> from huggingface_hub import hf_hub_download
+ >>> from PIL import Image
+
+ >>> processor = AutoProcessor.from_pretrained("microsoft/git-base-textvqa")
+ >>> model = AutoModelForCausalLM.from_pretrained("microsoft/git-base-textvqa")
+
+ >>> file_path = hf_hub_download(repo_id="nielsr/textvqa-sample", filename="bus.png", repo_type="dataset")
+ >>> image = Image.open(file_path).convert("RGB")
+
+ >>> pixel_values = processor(images=image, return_tensors="pt").pixel_values
+
+ >>> question = "what does the front of the bus say at the top?"
+
+ >>> input_ids = processor(text=question, add_special_tokens=False).input_ids
+ >>> input_ids = [processor.tokenizer.cls_token_id] + input_ids
+ >>> input_ids = torch.tensor(input_ids).unsqueeze(0)
+
+ >>> generated_ids = model.generate(pixel_values=pixel_values, input_ids=input_ids, max_length=50)
+ >>> print(processor.batch_decode(generated_ids, skip_special_tokens=True))
+ ['what does the front of the bus say at the top? special']
+ ```
+
+ Video captioning example:
+
+ ```python
+ >>> import av
+ >>> import numpy as np
+ >>> from PIL import Image
+ >>> from huggingface_hub import hf_hub_download
+ >>> from transformers import AutoProcessor, AutoModelForCausalLM
+
+ >>> processor = AutoProcessor.from_pretrained("microsoft/git-base-vatex")
+ >>> model = AutoModelForCausalLM.from_pretrained("microsoft/git-base-vatex")
+
+ >>> # set seed for reproducibility
+ >>> np.random.seed(45)
+
+
+ >>> def read_video_pyav(container, indices):
+ ... '''
+ ... Decode the video with PyAV decoder.
+ ... Args:
+ ... container (`av.container.input.InputContainer`): PyAV container.
+ ... indices (`list[int]`): List of frame indices to decode.
+ ... Returns:
+ ... result (np.ndarray): np array of decoded frames of shape (num_frames, height, width, 3).
+ ... '''
+ ... frames = []
+ ... container.seek(0)
+ ... start_index = indices[0]
+ ... end_index = indices[-1]
+ ... for i, frame in enumerate(container.decode(video=0)):
+ ... if i > end_index:
+ ... break
+ ... if i >= start_index and i in indices:
+ ... frames.append(frame)
+ ... return np.stack([x.to_ndarray(format="rgb24") for x in frames])
+
+
+ >>> def sample_frame_indices(clip_len, frame_sample_rate, seg_len):
+ ... '''
+ ... Sample a given number of frame indices from the video.
+ ... Args:
+ ... clip_len (`int`): Total number of frames to sample.
+ ... frame_sample_rate (`int`): Sample every n-th frame.
+ ... seg_len (`int`): Maximum allowed index of sample's last frame.
+ ... Returns:
+ ... indices (`list[int]`): List of sampled frame indices
+ ... '''
+ ... converted_len = int(clip_len * frame_sample_rate)
+ ... end_idx = np.random.randint(converted_len, seg_len)
+ ... start_idx = end_idx - converted_len
+ ... indices = np.linspace(start_idx, end_idx, num=clip_len)
+ ... indices = np.clip(indices, start_idx, end_idx - 1).astype(np.int64)
+ ... return indices
+
+
+ >>> # load video
+ >>> file_path = hf_hub_download(
+ ... repo_id="nielsr/video-demo", filename="eating_spaghetti.mp4", repo_type="dataset"
+ ... )
+ >>> container = av.open(file_path)
+
+ >>> # sample frames
+ >>> num_frames = model.config.num_image_with_embedding
+ >>> indices = sample_frame_indices(
+ ... clip_len=num_frames, frame_sample_rate=4, seg_len=container.streams.video[0].frames
+ ... )
+ >>> frames = read_video_pyav(container, indices)
+
+ >>> pixel_values = processor(images=list(frames), return_tensors="pt").pixel_values
+
+ >>> generated_ids = model.generate(pixel_values=pixel_values, max_length=50)
+
+ >>> print("Generated caption:", processor.batch_decode(generated_ids, skip_special_tokens=True))
+ Generated caption: ['a woman is sitting at a table and she is talking about the food she is holding.']
+ ```
+ """
+ if labels is not None:
+ use_cache = False
+
+ outputs: BaseModelOutputWithPast = self.git(
+ input_ids,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ pixel_values=pixel_values,
+ inputs_embeds=inputs_embeds,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ interpolate_pos_encoding=interpolate_pos_encoding,
+ **kwargs,
+ )
+
+ hidden_states = outputs.last_hidden_state
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
+ logits = self.output(hidden_states[:, slice_indices, :])
+
+ loss = None
+ if labels is not None:
+ # we are doing next-token prediction; shift prediction scores and input ids by one
+ num_image_tokens = self.git.encoder.layer[0].attention.self.image_patch_tokens
+ shifted_logits = logits[:, num_image_tokens:-1, :].contiguous()
+ labels = labels[:, 1:].contiguous()
+ loss = self.loss_function(
+ shifted_logits.view(-1, self.config.vocab_size),
+ labels.view(-1),
+ vocab_size=self.config.vocab_size,
+ **kwargs,
+ )
+
+ return CausalLMOutputWithPast(
+ loss=loss,
+ logits=logits,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ )
+
+ def prepare_inputs_for_generation(
+ self,
+ input_ids,
+ past_key_values=None,
+ pixel_values=None,
+ attention_mask=None,
+ use_cache=None,
+ is_first_iteration=False,
+ **kwargs,
+ ):
+ # Overwritten -- `git` has special `pixel_values` handling
+
+ model_inputs = super().prepare_inputs_for_generation(
+ input_ids,
+ past_key_values=past_key_values,
+ attention_mask=attention_mask,
+ use_cache=use_cache,
+ is_first_iteration=is_first_iteration,
+ **kwargs,
+ )
+
+ if is_first_iteration or not use_cache:
+ model_inputs["pixel_values"] = pixel_values
+
+ return model_inputs
+
+
+__all__ = ["GitForCausalLM", "GitModel", "GitPreTrainedModel", "GitVisionModel"]
diff --git a/third_party/transformers/src/transformers/models/git/processing_git.py b/third_party/transformers/src/transformers/models/git/processing_git.py
new file mode 100644
index 0000000000000000000000000000000000000000..2a4399b3c51c9e2c6e5d8e02b9664c99eec006bf
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/git/processing_git.py
@@ -0,0 +1,28 @@
+# Copyright 2022 The HuggingFace Inc. team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Image/Text processor class for GIT
+"""
+
+from ...processing_utils import ProcessorMixin
+from ...utils import auto_docstring
+
+
+@auto_docstring
+class GitProcessor(ProcessorMixin):
+ def __init__(self, image_processor, tokenizer):
+ super().__init__(image_processor, tokenizer)
+
+
+__all__ = ["GitProcessor"]
diff --git a/third_party/transformers/src/transformers/models/glm/__init__.py b/third_party/transformers/src/transformers/models/glm/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..0636c800beea6b02d16d13098bd7b13f11baf468
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/glm/__init__.py
@@ -0,0 +1,27 @@
+# Copyright 2024 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_glm import *
+ from .modeling_glm import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/glm/configuration_glm.py b/third_party/transformers/src/transformers/models/glm/configuration_glm.py
new file mode 100644
index 0000000000000000000000000000000000000000..98525012a23beed36b02561d97fed8ccc3600563
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/glm/configuration_glm.py
@@ -0,0 +1,83 @@
+# Copyright 2024 The GLM & ZhipuAI team and HuggingFace Inc. team. All rights reserved.
+#
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...modeling_rope_utils import RopeParameters
+from ...utils import auto_docstring
+
+
+@auto_docstring(checkpoint="THUDM/glm-4-9b-chat")
+@strict
+class GlmConfig(PreTrainedConfig):
+ r"""
+ Example:
+
+ ```python
+ >>> from transformers import GlmModel, GlmConfig
+ >>> # Initializing a Glm glm-4-9b-chat style configuration
+ >>> configuration = GlmConfig()
+ >>> # Initializing a model from the glm-4-9b-chat style configuration
+ >>> model = GlmModel(configuration)
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "glm"
+ keys_to_ignore_at_inference = ["past_key_values"]
+ base_model_tp_plan = {
+ "layers.*.self_attn.q_proj": "colwise",
+ "layers.*.self_attn.k_proj": "colwise",
+ "layers.*.self_attn.v_proj": "colwise",
+ "layers.*.self_attn.o_proj": "rowwise",
+ "layers.*.mlp.gate_up_proj": "colwise_gather_output", # we need to replicate here due to the `chunk` operation
+ "layers.*.mlp.down_proj": "rowwise_split_input", # input is replicated due to the `chunk` operation
+ }
+ base_model_pp_plan = {
+ "embed_tokens": (["input_ids"], ["inputs_embeds"]),
+ "layers": (["hidden_states", "attention_mask"], ["hidden_states"]),
+ "norm": (["hidden_states"], ["hidden_states"]),
+ }
+
+ vocab_size: int = 151552
+ hidden_size: int = 4096
+ intermediate_size: int = 13696
+ num_hidden_layers: int = 40
+ num_attention_heads: int = 32
+ num_key_value_heads: int | None = 2
+ head_dim: int | None = 128
+ hidden_act: str = "silu"
+ attention_dropout: float | int | None = 0.0
+ max_position_embeddings: int = 131072
+ initializer_range: float = 0.02
+ rms_norm_eps: float = 0.00000015625
+ use_cache: bool = True
+ tie_word_embeddings: bool = False
+ rope_parameters: RopeParameters | dict | None = None
+ pad_token_id: int | None = 151329
+ eos_token_id: int | list[int] | None = None
+ bos_token_id: int | None = None
+ attention_bias: bool = True
+
+ def __post_init__(self, **kwargs):
+ kwargs.setdefault("partial_rotary_factor", 0.5) # assign default for BC
+ if self.eos_token_id is None:
+ self.eos_token_id = [151329, 151336, 151338]
+ super().__post_init__(**kwargs)
+
+
+__all__ = ["GlmConfig"]
diff --git a/third_party/transformers/src/transformers/models/glm/convert_glm_weights_to_hf.py b/third_party/transformers/src/transformers/models/glm/convert_glm_weights_to_hf.py
new file mode 100644
index 0000000000000000000000000000000000000000..df1fd7537f4c2ed2dd3077efdb5ded0dd3e3974f
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/glm/convert_glm_weights_to_hf.py
@@ -0,0 +1,195 @@
+import argparse
+import json
+import os
+import re
+
+import torch
+from safetensors.torch import load_file
+from tokenizers import processors
+
+from transformers import GlmConfig, GlmForCausalLM, PreTrainedTokenizerFast
+
+
+# fmt: off
+# `None` means we drop the key
+STATE_DICT_MAPPING = {
+ # CausalLM keys
+ r"transformer.output_layer.weight": r"lm_head.weight",
+
+ # Model keys
+ r"transformer.embedding.word_embeddings.weight": r"model.embed_tokens.weight",
+ r"transformer.rotary_pos_emb.inv_freq": None,
+ r"transformer.encoder.final_layernorm.weight": r"model.norm.weight",
+
+ # Layers keys
+ r"transformer.encoder.layers.(\d+).input_layernorm.weight": r"model.layers.\1.input_layernorm.weight",
+ r"transformer.encoder.layers.(\d+).post_attention_layernorm.weight": r"model.layers.\1.post_attention_layernorm.weight",
+
+ # Attention keys
+ r"transformer.encoder.layers.(\d+).self_attention.dense.weight": r"model.layers.\1.self_attn.o_proj.weight",
+ # qkv_proj will later be split in q|k|v|_proj
+ r"transformer.encoder.layers.(\d+).self_attention.query_key_value.(weight|bias)": r"model.layers.\1.self_attn.qkv_proj.\2",
+
+ # MLP keys
+ r"transformer.encoder.layers.(\d+).mlp.dense_h_to_4h.weight": r"model.layers.\1.mlp.gate_up_proj.weight",
+ r"transformer.encoder.layers.(\d+).mlp.dense_4h_to_h.weight": r"model.layers.\1.mlp.down_proj.weight",
+}
+# fmt: on
+
+
+def load_weights(input_dir: str):
+ safetensor_files = [os.path.join(input_dir, x) for x in os.listdir(input_dir) if x.endswith(".safetensors")]
+ bin_files = [os.path.join(input_dir, x) for x in os.listdir(input_dir) if x.endswith(".bin")]
+
+ all_weights = {}
+
+ if safetensor_files:
+ safetensor_files = sorted(safetensor_files, key=lambda x: int(x.rsplit("-", 3)[1]))
+ for file in safetensor_files:
+ tensors = load_file(file)
+ all_weights.update(tensors)
+ return all_weights
+
+ elif bin_files:
+ bin_files = sorted(bin_files, key=lambda x: int(x.rsplit("-", 3)[1]))
+ for file in bin_files:
+ tensors = torch.load(file, map_location="cpu", weights_only=True)
+ all_weights.update(tensors)
+ return all_weights
+
+ else:
+ raise ValueError("No .safetensors or .bin files found in the specified directory.")
+
+
+def map_old_key_to_new(old_key):
+ for pattern, replacement in STATE_DICT_MAPPING.items():
+ if replacement is None:
+ if re.fullmatch(pattern, old_key):
+ return None
+ else:
+ new_key, n_replace = re.subn(pattern, replacement, old_key)
+ # Early exit of the loop
+ if n_replace > 0:
+ return new_key
+
+ raise ValueError(f"Key: {old_key} could not be mapped (check the mapping).")
+
+
+def convert_state_dict(original_state_dict: dict, config: GlmConfig):
+ new_dict = {}
+
+ head_dim = config.hidden_size // config.num_attention_heads
+ query_size = config.num_attention_heads * head_dim
+ kv_size = config.num_key_value_heads * head_dim
+
+ for old_key, value in original_state_dict.items():
+ new_key = map_old_key_to_new(old_key)
+ if new_key is None:
+ continue
+
+ if "qkv_proj." in new_key:
+ q_proj, k_proj, v_proj = (
+ value[:query_size, ...],
+ value[query_size : query_size + kv_size, ...],
+ value[query_size + kv_size :, ...],
+ )
+ new_dict[new_key.replace("qkv_proj.", "q_proj.")] = q_proj
+ new_dict[new_key.replace("qkv_proj.", "k_proj.")] = k_proj
+ new_dict[new_key.replace("qkv_proj.", "v_proj.")] = v_proj
+ else:
+ new_dict[new_key] = value
+ return new_dict
+
+
+def convert_config(original_config: dict):
+ key_mapping = {
+ "vocab_size": "padded_vocab_size",
+ "intermediate_size": "ffn_hidden_size",
+ "num_hidden_layers": "num_layers",
+ "max_position_embeddings": "seq_length",
+ "rms_norm_eps": "layernorm_epsilon",
+ "head_dim": "kv_channels",
+ "attention_bias": "add_qkv_bias",
+ }
+ similar_keys_to_keep = [
+ "num_attention_heads",
+ "hidden_size",
+ "attention_dropout",
+ "use_cache",
+ "eos_token_id",
+ "pad_token_id",
+ "tie_word_embeddings",
+ ]
+ new_config_kwargs = {k: original_config[v] for k, v in key_mapping.items()}
+ new_config_kwargs.update({k: v for k, v in original_config.items() if k in similar_keys_to_keep})
+ new_config_kwargs["num_key_value_heads"] = (
+ new_config_kwargs["num_attention_heads"]
+ if not original_config["multi_query_attention"]
+ else original_config["multi_query_group_num"]
+ )
+ new_config_kwargs["rope_theta"] = 10000.0 * getattr(original_config, "rope_ratio", 1)
+
+ new_config = GlmConfig(**new_config_kwargs)
+ return new_config
+
+
+def convert_glm_tokenizer(input_dir, use_post_processor=False):
+ fast_tok = PreTrainedTokenizerFast.from_pretrained(input_dir, model_input_names=["input_ids", "attention_mask"])
+ if use_post_processor:
+ fast_tok._tokenizer.post_processor = processors.Sequence(
+ [
+ processors.ByteLevel(trim_offsets=False),
+ processors.TemplateProcessing(
+ single="[gMASK]:0 :0 $A:0",
+ pair="[gMASK]:0 :0 $A:0 $B:1",
+ special_tokens=[("[gMASK]", 151331), ("", 151333)],
+ ),
+ ],
+ )
+ else:
+ fast_tok._tokenizer.post_processor = processors.Sequence(
+ [processors.ByteLevel(trim_offsets=False)],
+ )
+ return fast_tok
+
+
+def convert_glm_model(input_dir, output_dir, use_post_processor=False):
+ # Load and convert config
+ with open(os.path.join(input_dir, "config.json")) as f:
+ original_config = json.load(f)
+ config = convert_config(original_config)
+ config.save_pretrained(output_dir)
+
+ # Load and convert weights
+ original_state_dict = load_weights(input_dir)
+ new_dict = convert_state_dict(original_state_dict, config)
+ with torch.device("meta"):
+ model = GlmForCausalLM(config)
+ model.load_state_dict(new_dict, strict=True, assign=True)
+ model.save_pretrained(output_dir)
+
+ # Load and convert tokenizer
+ tokenizer = convert_glm_tokenizer(input_dir, use_post_processor)
+ tokenizer.save_pretrained(output_dir)
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "input_dir",
+ type=str,
+ help="Location of the local folder copied from the Hub.",
+ )
+ parser.add_argument(
+ "output_dir",
+ type=str,
+ help="Location to write HF model and tokenizer",
+ )
+ parser.add_argument(
+ "--use_post_processor",
+ action="store_true",
+ help="Whether to apply post processor with special tokens",
+ )
+
+ args = parser.parse_args()
+ convert_glm_model(args.input_dir, args.output_dir, args.use_post_processor)
diff --git a/third_party/transformers/src/transformers/models/glm/modeling_glm.py b/third_party/transformers/src/transformers/models/glm/modeling_glm.py
new file mode 100644
index 0000000000000000000000000000000000000000..712202580943634dc1250e15db321681aaffff35
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/glm/modeling_glm.py
@@ -0,0 +1,527 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/glm/modular_glm.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_glm.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2024 The GLM & ZhipuAI team and HuggingFace Inc. team. All rights reserved.
+#
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from collections.abc import Callable
+from typing import Optional
+
+import torch
+import torch.nn as nn
+
+from ...activations import ACT2FN
+from ...cache_utils import Cache, DynamicCache
+from ...generation import GenerationMixin
+from ...integrations import use_kernel_forward_from_hub, use_kernelized_func
+from ...masking_utils import create_causal_mask
+from ...modeling_layers import (
+ GenericForSequenceClassification,
+ GenericForTokenClassification,
+ GradientCheckpointingLayer,
+)
+from ...modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast
+from ...modeling_rope_utils import ROPE_INIT_FUNCTIONS, dynamic_rope_update
+from ...modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
+from ...processing_utils import Unpack
+from ...utils import TransformersKwargs, auto_docstring, can_return_tuple
+from ...utils.generic import maybe_autocast, merge_with_config_defaults
+from ...utils.output_capturing import capture_outputs
+from .configuration_glm import GlmConfig
+
+
+class GlmMLP(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+
+ self.config = config
+ self.gate_up_proj = nn.Linear(config.hidden_size, 2 * config.intermediate_size, bias=False)
+ self.down_proj = nn.Linear(config.intermediate_size, config.hidden_size, bias=False)
+ self.activation_fn = ACT2FN[config.hidden_act]
+
+ def forward(self, hidden_states: torch.FloatTensor) -> torch.FloatTensor:
+ up_states = self.gate_up_proj(hidden_states)
+
+ gate, up_states = up_states.chunk(2, dim=-1)
+ up_states = up_states * self.activation_fn(gate)
+
+ return self.down_proj(up_states)
+
+
+class GlmRotaryEmbedding(nn.Module):
+ inv_freq: torch.Tensor # fix linting for `register_buffer`
+
+ def __init__(self, config: GlmConfig, device=None):
+ super().__init__()
+ self.max_seq_len_cached = config.max_position_embeddings
+ self.original_max_seq_len = config.max_position_embeddings
+
+ self.config = config
+
+ self.rope_type = self.config.rope_parameters["rope_type"]
+ rope_init_fn: Callable = self.compute_default_rope_parameters
+ if self.rope_type != "default":
+ rope_init_fn = ROPE_INIT_FUNCTIONS[self.rope_type]
+ inv_freq, self.attention_scaling = rope_init_fn(self.config, device)
+
+ self.register_buffer("inv_freq", inv_freq, persistent=False)
+ self.register_buffer("original_inv_freq", inv_freq.clone(), persistent=False)
+
+ @staticmethod
+ def compute_default_rope_parameters(
+ config: GlmConfig | None = None,
+ device: Optional["torch.device"] = None,
+ seq_len: int | None = None,
+ ) -> tuple["torch.Tensor", float]:
+ """
+ Computes the inverse frequencies according to the original RoPE implementation
+ Args:
+ config ([`~transformers.PreTrainedConfig`]):
+ The model configuration.
+ device (`torch.device`):
+ The device to use for initialization of the inverse frequencies.
+ seq_len (`int`, *optional*):
+ The current sequence length. Unused for this type of RoPE.
+ Returns:
+ Tuple of (`torch.Tensor`, `float`), containing the inverse frequencies for the RoPE embeddings and the
+ post-processing scaling factor applied to the computed cos/sin (unused in this type of RoPE).
+ """
+ base = config.rope_parameters["rope_theta"]
+ partial_rotary_factor = config.rope_parameters.get("partial_rotary_factor", 1.0)
+ head_dim = getattr(config, "head_dim", None) or config.hidden_size // config.num_attention_heads
+ dim = int(head_dim * partial_rotary_factor)
+
+ attention_factor = 1.0 # Unused in this type of RoPE
+
+ # Compute the inverse frequencies
+ inv_freq = 1.0 / (
+ base ** (torch.arange(0, dim, 2, dtype=torch.int64).to(device=device, dtype=torch.float) / dim)
+ )
+ return inv_freq, attention_factor
+
+ @torch.no_grad()
+ @dynamic_rope_update # power user: used with advanced RoPE types (e.g. dynamic rope)
+ def forward(self, x, position_ids):
+ inv_freq_expanded = self.inv_freq[None, :, None].float().expand(position_ids.shape[0], -1, 1).to(x.device)
+ position_ids_expanded = position_ids[:, None, :].float()
+
+ device_type = x.device.type if isinstance(x.device.type, str) and x.device.type != "mps" else "cpu"
+ with maybe_autocast(device_type=device_type, enabled=False): # Force float32
+ freqs = (inv_freq_expanded.float() @ position_ids_expanded.float()).transpose(1, 2)
+ emb = torch.cat((freqs, freqs), dim=-1)
+ cos = emb.cos() * self.attention_scaling
+ sin = emb.sin() * self.attention_scaling
+
+ return cos.to(dtype=x.dtype), sin.to(dtype=x.dtype)
+
+
+def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
+ """
+ This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
+ num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
+ """
+ batch, num_key_value_heads, slen, head_dim = hidden_states.shape
+ if n_rep == 1:
+ return hidden_states
+ hidden_states = hidden_states[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim)
+ return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
+
+
+def eager_attention_forward(
+ module: nn.Module,
+ query: torch.Tensor,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ attention_mask: torch.Tensor | None,
+ scaling: float,
+ dropout: float = 0.0,
+ **kwargs: Unpack[TransformersKwargs],
+):
+ key_states = repeat_kv(key, module.num_key_value_groups)
+ value_states = repeat_kv(value, module.num_key_value_groups)
+
+ attn_weights = torch.matmul(query, key_states.transpose(2, 3)) * scaling
+ if attention_mask is not None:
+ attn_weights = attn_weights + attention_mask
+
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query.dtype)
+ attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
+ attn_output = torch.matmul(attn_weights, value_states)
+ attn_output = attn_output.transpose(1, 2).contiguous()
+
+ return attn_output, attn_weights
+
+
+def rotate_half(x):
+ """Rotates half the hidden dims of the input."""
+ x1 = x[..., 0::2]
+ x2 = x[..., 1::2]
+ return torch.stack((-x2, x1), dim=-1).flatten(-2)
+
+
+def apply_rotary_pos_emb(q, k, cos, sin, unsqueeze_dim=1):
+ """Applies Rotary Position Embedding to the query and key tensors.
+
+ Args:
+ q (`torch.Tensor`): The query tensor.
+ k (`torch.Tensor`): The key tensor.
+ cos (`torch.Tensor`): The cosine part of the rotary embedding.
+ sin (`torch.Tensor`): The sine part of the rotary embedding.
+ unsqueeze_dim (`int`, *optional*, defaults to 1):
+ The 'unsqueeze_dim' argument specifies the dimension along which to unsqueeze cos[position_ids] and
+ sin[position_ids] so that they can be properly broadcasted to the dimensions of q and k. For example, note
+ that cos[position_ids] and sin[position_ids] have the shape [batch_size, seq_len, head_dim]. Then, if q and
+ k have the shape [batch_size, heads, seq_len, head_dim], then setting unsqueeze_dim=1 makes
+ cos[position_ids] and sin[position_ids] broadcastable to the shapes of q and k. Similarly, if q and k have
+ the shape [batch_size, seq_len, heads, head_dim], then set unsqueeze_dim=2.
+ Returns:
+ `tuple(torch.Tensor)` comprising of the query and key tensors rotated using the Rotary Position Embedding.
+ """
+ cos = cos.unsqueeze(unsqueeze_dim)
+ sin = sin.unsqueeze(unsqueeze_dim)
+
+ # Interleave them instead of usual shape
+ cos = cos[..., : cos.shape[-1] // 2].repeat_interleave(2, dim=-1)
+ sin = sin[..., : sin.shape[-1] // 2].repeat_interleave(2, dim=-1)
+
+ # Keep half or full tensor for later concatenation
+ rotary_dim = cos.shape[-1]
+ q_rot, q_pass = q[..., :rotary_dim], q[..., rotary_dim:]
+ k_rot, k_pass = k[..., :rotary_dim], k[..., rotary_dim:]
+
+ # Apply rotary embeddings on the first half or full tensor
+ q_embed = (q_rot * cos) + (rotate_half(q_rot) * sin)
+ k_embed = (k_rot * cos) + (rotate_half(k_rot) * sin)
+
+ # Concatenate back to full shape
+ q_embed = torch.cat([q_embed, q_pass], dim=-1)
+ k_embed = torch.cat([k_embed, k_pass], dim=-1)
+ return q_embed, k_embed
+
+
+@use_kernelized_func(apply_rotary_pos_emb)
+class GlmAttention(nn.Module):
+ """Multi-headed attention from 'Attention Is All You Need' paper"""
+
+ def __init__(self, config: GlmConfig, layer_idx: int | None = None):
+ super().__init__()
+ self.config = config
+ self.layer_idx = layer_idx
+ self.head_dim = getattr(config, "head_dim", config.hidden_size // config.num_attention_heads)
+ self.num_key_value_groups = config.num_attention_heads // config.num_key_value_heads
+ self.scaling = self.head_dim**-0.5
+ self.attention_dropout = config.attention_dropout
+ self.is_causal = True
+
+ self.q_proj = nn.Linear(
+ config.hidden_size, config.num_attention_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.k_proj = nn.Linear(
+ config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.v_proj = nn.Linear(
+ config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.o_proj = nn.Linear(config.num_attention_heads * self.head_dim, config.hidden_size, bias=False)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
+ attention_mask: torch.Tensor | None = None,
+ past_key_values: Cache | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple[torch.Tensor, torch.Tensor]:
+ input_shape = hidden_states.shape[:-1]
+ hidden_shape = (*input_shape, -1, self.head_dim)
+
+ query_states = self.q_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+ key_states = self.k_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+ value_states = self.v_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+
+ cos, sin = position_embeddings
+ query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin)
+
+ if past_key_values is not None:
+ key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx)
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ query_states,
+ key_states,
+ value_states,
+ attention_mask,
+ dropout=0.0 if not self.training else self.attention_dropout,
+ scaling=self.scaling,
+ **kwargs,
+ )
+
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
+ attn_output = self.o_proj(attn_output)
+ return attn_output, attn_weights
+
+
+@use_kernel_forward_from_hub("RMSNorm")
+class GlmRMSNorm(nn.Module):
+ def __init__(self, hidden_size, eps: float = 1e-6) -> None:
+ """
+ GlmRMSNorm is equivalent to T5LayerNorm
+ """
+ super().__init__()
+ self.weight = nn.Parameter(torch.ones(hidden_size))
+ self.variance_epsilon = eps
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ input_dtype = hidden_states.dtype
+ hidden_states = hidden_states.to(torch.float32)
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
+ hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
+ return self.weight * hidden_states.to(input_dtype)
+
+ def extra_repr(self):
+ return f"{tuple(self.weight.shape)}, eps={self.variance_epsilon}"
+
+
+class GlmDecoderLayer(GradientCheckpointingLayer):
+ def __init__(self, config: GlmConfig, layer_idx: int):
+ super().__init__()
+ self.hidden_size = config.hidden_size
+
+ self.self_attn = GlmAttention(config=config, layer_idx=layer_idx)
+
+ self.mlp = GlmMLP(config)
+ self.input_layernorm = GlmRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.post_attention_layernorm = GlmRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ use_cache: bool | None = False,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> torch.Tensor:
+ residual = hidden_states
+ hidden_states = self.input_layernorm(hidden_states)
+ # Self Attention
+ hidden_states, _ = self.self_attn(
+ hidden_states=hidden_states,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ position_embeddings=position_embeddings,
+ **kwargs,
+ )
+ hidden_states = residual + hidden_states
+
+ # Fully Connected
+ residual = hidden_states
+ hidden_states = self.post_attention_layernorm(hidden_states)
+ hidden_states = self.mlp(hidden_states)
+ hidden_states = residual + hidden_states
+ return hidden_states
+
+
+@auto_docstring
+class GlmPreTrainedModel(PreTrainedModel):
+ config: GlmConfig
+ base_model_prefix = "model"
+ supports_gradient_checkpointing = True
+ _no_split_modules = ["GlmDecoderLayer"]
+ _skip_keys_device_placement = ["past_key_values"]
+ _supports_flash_attn = True
+ _supports_sdpa = True
+ _supports_flex_attn = True
+
+ _can_compile_fullgraph = True
+ _supports_attention_backend = True
+ _can_record_outputs = {
+ "hidden_states": GlmDecoderLayer,
+ "attentions": GlmAttention,
+ }
+
+
+@auto_docstring
+class GlmModel(GlmPreTrainedModel):
+ def __init__(self, config: GlmConfig):
+ super().__init__(config)
+ self.padding_idx = config.pad_token_id
+ self.vocab_size = config.vocab_size
+
+ self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)
+ self.layers = nn.ModuleList(
+ [GlmDecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
+ )
+ self.norm = GlmRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.rotary_emb = GlmRotaryEmbedding(config=config)
+ self.gradient_checkpointing = False
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @merge_with_config_defaults
+ @capture_outputs
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ use_cache: bool | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> BaseModelOutputWithPast:
+ 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 None:
+ inputs_embeds: torch.Tensor = self.embed_tokens(input_ids)
+
+ if use_cache and past_key_values is None:
+ past_key_values = DynamicCache(config=self.config)
+
+ if position_ids is None:
+ past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
+ position_ids = torch.arange(inputs_embeds.shape[1], device=inputs_embeds.device) + past_seen_tokens
+ position_ids = position_ids.unsqueeze(0)
+
+ causal_mask = create_causal_mask(
+ config=self.config,
+ inputs_embeds=inputs_embeds,
+ attention_mask=attention_mask,
+ past_key_values=past_key_values,
+ position_ids=position_ids,
+ )
+
+ hidden_states = inputs_embeds
+ position_embeddings = self.rotary_emb(hidden_states, position_ids=position_ids)
+
+ for decoder_layer in self.layers[: self.config.num_hidden_layers]:
+ hidden_states = decoder_layer(
+ hidden_states,
+ attention_mask=causal_mask,
+ position_embeddings=position_embeddings,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ hidden_states = self.norm(hidden_states)
+ return BaseModelOutputWithPast(
+ last_hidden_state=hidden_states,
+ past_key_values=past_key_values,
+ )
+
+
+@auto_docstring
+class GlmForCausalLM(GlmPreTrainedModel, GenerationMixin):
+ _tied_weights_keys = {"lm_head.weight": "model.embed_tokens.weight"}
+ _tp_plan = {"lm_head": "colwise_gather_output"}
+ _pp_plan = {"lm_head": (["hidden_states"], ["logits"])}
+
+ def __init__(self, config):
+ super().__init__(config)
+ self.model = GlmModel(config)
+ self.vocab_size = config.vocab_size
+ self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> CausalLMOutputWithPast:
+ r"""
+ Example:
+
+ ```python
+ >>> from transformers import AutoTokenizer, GlmForCausalLM
+
+ >>> model = GlmForCausalLM.from_pretrained("meta-glm/Glm-2-7b-hf")
+ >>> tokenizer = AutoTokenizer.from_pretrained("meta-glm/Glm-2-7b-hf")
+
+ >>> prompt = "Hey, are you conscious? Can you talk to me?"
+ >>> inputs = tokenizer(prompt, return_tensors="pt")
+
+ >>> # Generate
+ >>> generate_ids = model.generate(inputs.input_ids, max_length=30)
+ >>> tokenizer.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]
+ "Hey, are you conscious? Can you talk to me?\nI'm not conscious, but I can talk to you."
+ ```"""
+ outputs: BaseModelOutputWithPast = self.model(
+ input_ids=input_ids,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ hidden_states = outputs.last_hidden_state
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(logits=logits, labels=labels, vocab_size=self.config.vocab_size, **kwargs)
+
+ return CausalLMOutputWithPast(
+ loss=loss,
+ logits=logits,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ )
+
+
+class GlmForSequenceClassification(GenericForSequenceClassification, GlmPreTrainedModel):
+ pass
+
+
+class GlmForTokenClassification(GenericForTokenClassification, GlmPreTrainedModel):
+ pass
+
+
+__all__ = [
+ "GlmPreTrainedModel",
+ "GlmModel",
+ "GlmForCausalLM",
+ "GlmForSequenceClassification",
+ "GlmForTokenClassification",
+]
diff --git a/third_party/transformers/src/transformers/models/glm/modular_glm.py b/third_party/transformers/src/transformers/models/glm/modular_glm.py
new file mode 100644
index 0000000000000000000000000000000000000000..8cc9627bfa9e270c5d1f0b39d2e73e7fa40d2fe9
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/glm/modular_glm.py
@@ -0,0 +1,146 @@
+# Copyright 2024 The GLM & ZhipuAI team and HuggingFace Inc. team. All rights reserved.
+#
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import Optional
+
+import torch
+import torch.nn as nn
+
+from ...utils import logging
+from ..llama.modeling_llama import (
+ LlamaAttention,
+ LlamaForCausalLM,
+ LlamaForSequenceClassification,
+ LlamaForTokenClassification,
+ LlamaRotaryEmbedding,
+)
+from ..phi3.modeling_phi3 import Phi3MLP
+from .configuration_glm import GlmConfig
+
+
+logger = logging.get_logger(__name__)
+
+_CHECKPOINT_FOR_DOC = "THUDM/glm-4-9b"
+
+
+class GlmMLP(Phi3MLP):
+ pass
+
+
+class GlmRotaryEmbedding(LlamaRotaryEmbedding):
+ @staticmethod
+ def compute_default_rope_parameters(
+ config: GlmConfig | None = None,
+ device: Optional["torch.device"] = None,
+ seq_len: int | None = None,
+ ) -> tuple["torch.Tensor", float]:
+ """
+ Computes the inverse frequencies according to the original RoPE implementation
+ Args:
+ config ([`~transformers.PreTrainedConfig`]):
+ The model configuration.
+ device (`torch.device`):
+ The device to use for initialization of the inverse frequencies.
+ seq_len (`int`, *optional*):
+ The current sequence length. Unused for this type of RoPE.
+ Returns:
+ Tuple of (`torch.Tensor`, `float`), containing the inverse frequencies for the RoPE embeddings and the
+ post-processing scaling factor applied to the computed cos/sin (unused in this type of RoPE).
+ """
+ base = config.rope_parameters["rope_theta"]
+ partial_rotary_factor = config.rope_parameters.get("partial_rotary_factor", 1.0)
+ head_dim = getattr(config, "head_dim", None) or config.hidden_size // config.num_attention_heads
+ dim = int(head_dim * partial_rotary_factor)
+
+ attention_factor = 1.0 # Unused in this type of RoPE
+
+ # Compute the inverse frequencies
+ inv_freq = 1.0 / (
+ base ** (torch.arange(0, dim, 2, dtype=torch.int64).to(device=device, dtype=torch.float) / dim)
+ )
+ return inv_freq, attention_factor
+
+
+def rotate_half(x):
+ """Rotates half the hidden dims of the input."""
+ x1 = x[..., 0::2]
+ x2 = x[..., 1::2]
+ return torch.stack((-x2, x1), dim=-1).flatten(-2)
+
+
+def apply_rotary_pos_emb(q, k, cos, sin, unsqueeze_dim=1):
+ """Applies Rotary Position Embedding to the query and key tensors.
+
+ Args:
+ q (`torch.Tensor`): The query tensor.
+ k (`torch.Tensor`): The key tensor.
+ cos (`torch.Tensor`): The cosine part of the rotary embedding.
+ sin (`torch.Tensor`): The sine part of the rotary embedding.
+ unsqueeze_dim (`int`, *optional*, defaults to 1):
+ The 'unsqueeze_dim' argument specifies the dimension along which to unsqueeze cos[position_ids] and
+ sin[position_ids] so that they can be properly broadcasted to the dimensions of q and k. For example, note
+ that cos[position_ids] and sin[position_ids] have the shape [batch_size, seq_len, head_dim]. Then, if q and
+ k have the shape [batch_size, heads, seq_len, head_dim], then setting unsqueeze_dim=1 makes
+ cos[position_ids] and sin[position_ids] broadcastable to the shapes of q and k. Similarly, if q and k have
+ the shape [batch_size, seq_len, heads, head_dim], then set unsqueeze_dim=2.
+ Returns:
+ `tuple(torch.Tensor)` comprising of the query and key tensors rotated using the Rotary Position Embedding.
+ """
+ cos = cos.unsqueeze(unsqueeze_dim)
+ sin = sin.unsqueeze(unsqueeze_dim)
+
+ # Interleave them instead of usual shape
+ cos = cos[..., : cos.shape[-1] // 2].repeat_interleave(2, dim=-1)
+ sin = sin[..., : sin.shape[-1] // 2].repeat_interleave(2, dim=-1)
+
+ # Keep half or full tensor for later concatenation
+ rotary_dim = cos.shape[-1]
+ q_rot, q_pass = q[..., :rotary_dim], q[..., rotary_dim:]
+ k_rot, k_pass = k[..., :rotary_dim], k[..., rotary_dim:]
+
+ # Apply rotary embeddings on the first half or full tensor
+ q_embed = (q_rot * cos) + (rotate_half(q_rot) * sin)
+ k_embed = (k_rot * cos) + (rotate_half(k_rot) * sin)
+
+ # Concatenate back to full shape
+ q_embed = torch.cat([q_embed, q_pass], dim=-1)
+ k_embed = torch.cat([k_embed, k_pass], dim=-1)
+ return q_embed, k_embed
+
+
+class GlmAttention(LlamaAttention):
+ def __init__(self, config: GlmConfig, layer_idx: int | None = None):
+ super().__init__(config, layer_idx)
+ self.o_proj = nn.Linear(config.num_attention_heads * self.head_dim, config.hidden_size, bias=False)
+
+
+class GlmForCausalLM(LlamaForCausalLM):
+ pass
+
+
+class GlmForSequenceClassification(LlamaForSequenceClassification):
+ pass
+
+
+class GlmForTokenClassification(LlamaForTokenClassification):
+ pass
+
+
+__all__ = [
+ "GlmPreTrainedModel", # noqa: F822
+ "GlmModel", # noqa: F822
+ "GlmForCausalLM",
+ "GlmForSequenceClassification",
+ "GlmForTokenClassification",
+]
diff --git a/third_party/transformers/src/transformers/models/glm46v/__init__.py b/third_party/transformers/src/transformers/models/glm46v/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..5d6c4b6b007b6c3299545e6c37dd6d50145384d6
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/glm46v/__init__.py
@@ -0,0 +1,31 @@
+# Copyright 2025 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_glm46v import *
+ from .image_processing_glm46v import *
+ from .image_processing_pil_glm46v import *
+ from .modeling_glm46v import *
+ from .processing_glm46v import *
+ from .video_processing_glm46v import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/glm46v/configuration_glm46v.py b/third_party/transformers/src/transformers/models/glm46v/configuration_glm46v.py
new file mode 100644
index 0000000000000000000000000000000000000000..95c62c7c99791693131ba38914c874e22654cc45
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/glm46v/configuration_glm46v.py
@@ -0,0 +1,85 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/glm46v/modular_glm46v.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_glm46v.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2025 the HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...utils import auto_docstring
+from ..auto import CONFIG_MAPPING, AutoConfig
+
+
+@auto_docstring(checkpoint="zai-org/GLM-4.1V-9B-Thinking")
+@strict
+class Glm46VConfig(PreTrainedConfig):
+ r"""
+ image_start_token_id (`int`, *optional*, defaults to 151339):
+ The image start token index to encode the start of image.
+ image_end_token_id (`int`, *optional*, defaults to 151340):
+ The image end token index to encode the end of image.
+ video_start_token_id (`int`, *optional*, defaults to 151361):
+ The video start token index to encode the start of video.
+ video_end_token_id (`int`, *optional*, defaults to 151362):
+ The video end token index to encode the end of video.
+
+ ```python
+ >>> from transformers import Glm46VForConditionalGeneration, Glm46VConfig
+
+ >>> # Initializing a GLM-4.6V style configuration
+ >>> configuration = Glm46VConfig()
+
+ >>> # Initializing a model from the GLM-4.6V style configuration
+ >>> model = Glm4vForConditionalGeneration(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "glm46v"
+ sub_configs = {"text_config": AutoConfig, "vision_config": AutoConfig}
+ keys_to_ignore_at_inference = ["past_key_values"]
+
+ text_config: dict | PreTrainedConfig | None = None
+ vision_config: dict | PreTrainedConfig | None = None
+ image_token_id: int = 151343
+ video_token_id: int = 151344
+ image_start_token_id: int = 151339
+ image_end_token_id: int = 151340
+ video_start_token_id: int = 151361
+ video_end_token_id: int = 151362
+ tie_word_embeddings: bool = False
+
+ def __post_init__(self, **kwargs):
+ if isinstance(self.vision_config, dict):
+ self.vision_config["model_type"] = self.vision_config.get("model_type", "glm4v_vision")
+ self.vision_config = CONFIG_MAPPING[self.vision_config["model_type"]](**self.vision_config)
+ elif self.vision_config is None:
+ self.vision_config = CONFIG_MAPPING["glm4v_vision"]()
+
+ if isinstance(self.text_config, dict):
+ self.text_config["model_type"] = self.text_config.get("model_type", "glm4v_text")
+ self.text_config = CONFIG_MAPPING[self.text_config["model_type"]](**self.text_config)
+ elif self.text_config is None:
+ self.text_config = CONFIG_MAPPING["glm4v_text"]()
+
+ super().__post_init__(**kwargs)
+
+
+__all__ = ["Glm46VConfig"]
diff --git a/third_party/transformers/src/transformers/models/glm46v/image_processing_glm46v.py b/third_party/transformers/src/transformers/models/glm46v/image_processing_glm46v.py
new file mode 100644
index 0000000000000000000000000000000000000000..9ed4ceba6d6e5216d55447d8ef0d3e2dfb49dce5
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/glm46v/image_processing_glm46v.py
@@ -0,0 +1,260 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/glm46v/modular_glm46v.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_glm46v.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2025 the HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import math
+
+import torch
+from torchvision.transforms.v2 import functional as tvF
+
+from ...image_processing_backends import TorchvisionBackend
+from ...image_processing_utils import BatchFeature
+from ...image_transforms import group_images_by_shape, reorder_images
+from ...image_utils import OPENAI_CLIP_MEAN, OPENAI_CLIP_STD, ImageInput, PILImageResampling, SizeDict
+from ...processing_utils import ImagesKwargs, Unpack
+from ...utils import TensorType, auto_docstring
+
+
+class Glm46VImageProcessorKwargs(ImagesKwargs, total=False):
+ """
+ patch_size (`int`, *optional*, defaults to 14):
+ The spatial patch size of the vision encoder.
+ temporal_patch_size (`int`, *optional*, defaults to 2):
+ The temporal patch size of the vision encoder.
+ merge_size (`int`, *optional*, defaults to 2):
+ The merge size of the vision encoder to llm encoder.
+ """
+
+ patch_size: int
+ temporal_patch_size: int
+ merge_size: int
+
+
+def smart_resize(
+ num_frames: int,
+ height: int,
+ width: int,
+ temporal_factor: int = 2,
+ factor: int = 28,
+ min_pixels: int = 112 * 112,
+ max_pixels: int = 14 * 14 * 2 * 2 * 2 * 6144,
+):
+ if num_frames < temporal_factor:
+ raise ValueError(f"t:{num_frames} must be larger than temporal_factor:{temporal_factor}")
+ if height < factor or width < factor:
+ scale = max(factor / height, factor / width)
+ height = int(height * scale)
+ width = int(width * scale)
+
+ if max(height, width) / min(height, width) > 200:
+ raise ValueError(
+ f"absolute aspect ratio must be smaller than 200, got {max(height, width) / min(height, width)}"
+ )
+ h_bar = round(height / factor) * factor
+ w_bar = round(width / factor) * factor
+ t_bar = round(num_frames / temporal_factor) * temporal_factor
+
+ if t_bar * h_bar * w_bar > max_pixels:
+ beta = math.sqrt((num_frames * height * width) / max_pixels)
+ h_bar = max(factor, math.floor(height / beta / factor) * factor)
+ w_bar = max(factor, math.floor(width / beta / factor) * factor)
+ elif t_bar * h_bar * w_bar < min_pixels:
+ beta = math.sqrt(min_pixels / (num_frames * height * width))
+ h_bar = math.ceil(height * beta / factor) * factor
+ w_bar = math.ceil(width * beta / factor) * factor
+
+ return h_bar, w_bar
+
+
+@auto_docstring
+class Glm46VImageProcessor(TorchvisionBackend):
+ do_resize = True
+ resample = PILImageResampling.BICUBIC
+ size = {"shortest_edge": 112 * 112, "longest_edge": 28 * 28 * 15000}
+ default_to_square = False
+ do_rescale = True
+ rescale_factor = 1 / 255
+ do_normalize = True
+ image_mean = OPENAI_CLIP_MEAN
+ image_std = OPENAI_CLIP_STD
+ do_convert_rgb = True
+ patch_size = 14
+ temporal_patch_size = 2
+ merge_size = 2
+ valid_kwargs = Glm46VImageProcessorKwargs
+ model_input_names = ["pixel_values", "image_grid_thw"]
+
+ def __init__(self, **kwargs: Unpack[Glm46VImageProcessorKwargs]):
+ super().__init__(**kwargs)
+ if self.size is not None:
+ if not self.size.shortest_edge or not self.size.longest_edge:
+ raise ValueError("size must contain 'shortest_edge' and 'longest_edge' keys.")
+
+ @auto_docstring
+ def preprocess(self, images: ImageInput, **kwargs: Unpack[Glm46VImageProcessorKwargs]) -> BatchFeature:
+ return super().preprocess(images, **kwargs)
+
+ def _standardize_kwargs(self, **kwargs) -> dict:
+ """
+ Update kwargs that need further processing before being validated
+ Can be overridden by subclasses to customize the processing of kwargs.
+ """
+ kwargs = super()._standardize_kwargs(**kwargs)
+ size = kwargs.get("size", self.size)
+ if not size.shortest_edge or not size.longest_edge:
+ raise ValueError("size must contain 'shortest_edge' and 'longest_edge' keys.")
+
+ return kwargs
+
+ def _preprocess(
+ self,
+ images: list["torch.Tensor"],
+ do_resize: bool,
+ size: SizeDict,
+ resample: "PILImageResampling | tvF.InterpolationMode | int | None",
+ do_rescale: bool,
+ rescale_factor: float,
+ do_normalize: bool,
+ image_mean: float | list[float] | None,
+ image_std: float | list[float] | None,
+ patch_size: int,
+ temporal_patch_size: int,
+ merge_size: int,
+ disable_grouping: bool | None,
+ return_tensors: str | TensorType | None,
+ **kwargs,
+ ) -> BatchFeature:
+ """
+ Preprocess an image or batch of images.
+ """
+
+ grouped_images, grouped_images_index = group_images_by_shape(images, disable_grouping=disable_grouping)
+ resized_images_grouped = {}
+ for shape, stacked_images in grouped_images.items():
+ height, width = stacked_images.shape[-2:]
+ if do_resize:
+ resized_height, resized_width = smart_resize(
+ num_frames=temporal_patch_size,
+ height=height,
+ width=width,
+ temporal_factor=temporal_patch_size,
+ factor=patch_size * merge_size,
+ min_pixels=size.shortest_edge,
+ max_pixels=size.longest_edge,
+ )
+ stacked_images = self.resize(
+ stacked_images,
+ size=SizeDict(height=resized_height, width=resized_width),
+ resample=resample,
+ )
+ resized_images_grouped[shape] = stacked_images
+
+ resized_images = reorder_images(resized_images_grouped, grouped_images_index)
+
+ grouped_images, grouped_images_index = group_images_by_shape(resized_images, disable_grouping=disable_grouping)
+ processed_images_grouped = {}
+ processed_grids = {}
+
+ for shape, stacked_images in grouped_images.items():
+ resized_height, resized_width = stacked_images.shape[-2:]
+
+ patches = self.rescale_and_normalize(
+ stacked_images, do_rescale, rescale_factor, do_normalize, image_mean, image_std
+ )
+ if patches.ndim == 4: # (B, C, H, W)
+ patches = patches.unsqueeze(1) # (B, T=1, C, H, W)
+
+ if patches.shape[1] % temporal_patch_size != 0:
+ repeats = patches[:, -1:].repeat(
+ 1, temporal_patch_size - (patches.shape[1] % temporal_patch_size), 1, 1, 1
+ )
+ patches = torch.cat([patches, repeats], dim=1)
+
+ batch_size, t_len, channel = patches.shape[:3]
+ grid_t = t_len // temporal_patch_size
+ grid_h, grid_w = resized_height // patch_size, resized_width // patch_size
+
+ patches = patches.view(
+ batch_size,
+ grid_t,
+ temporal_patch_size,
+ channel,
+ grid_h // merge_size,
+ merge_size,
+ patch_size,
+ grid_w // merge_size,
+ merge_size,
+ patch_size,
+ )
+ # (B, grid_t, gh, gw, mh, mw, C, tp, ph, pw)
+ patches = patches.permute(0, 1, 4, 7, 5, 8, 3, 2, 6, 9)
+
+ flatten_patches = patches.reshape(
+ batch_size,
+ grid_t * grid_h * grid_w,
+ channel * temporal_patch_size * patch_size * patch_size,
+ )
+
+ processed_images_grouped[shape] = flatten_patches
+ processed_grids[shape] = [[grid_t, grid_h, grid_w]] * batch_size
+
+ processed_images = reorder_images(processed_images_grouped, grouped_images_index)
+ processed_grids = reorder_images(processed_grids, grouped_images_index)
+
+ pixel_values = torch.cat(processed_images, dim=0)
+ image_grid_thw = torch.tensor(processed_grids)
+
+ return BatchFeature(
+ data={"pixel_values": pixel_values, "image_grid_thw": image_grid_thw}, tensor_type=return_tensors
+ )
+
+ def get_number_of_image_patches(self, height: int, width: int, images_kwargs=None):
+ """
+ A utility that returns number of image patches for a given image size.
+
+ Args:
+ height (`int`):
+ Height of the input image.
+ width (`int`):
+ Width of the input image.
+ images_kwargs (`dict`, *optional*)
+ Any kwargs to override defaults of the image processor.
+ Returns:
+ `int`: Number of image patches per image.
+ """
+ patch_size = images_kwargs.get("patch_size", self.patch_size)
+ merge_size = images_kwargs.get("merge_size", self.merge_size)
+ size = images_kwargs.get("size", self.size)
+
+ factor = patch_size * merge_size
+ resized_height, resized_width = smart_resize(
+ num_frames=self.temporal_patch_size,
+ height=height,
+ width=width,
+ factor=factor,
+ min_pixels=size["shortest_edge"],
+ max_pixels=size["longest_edge"],
+ temporal_factor=self.temporal_patch_size,
+ )
+ grid_h, grid_w = resized_height // patch_size, resized_width // patch_size
+ return grid_h * grid_w
+
+
+__all__ = ["Glm46VImageProcessor"]
diff --git a/third_party/transformers/src/transformers/models/glm46v/image_processing_pil_glm46v.py b/third_party/transformers/src/transformers/models/glm46v/image_processing_pil_glm46v.py
new file mode 100644
index 0000000000000000000000000000000000000000..5601e732c2b36002611ef3056ca7f3146acdac67
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/glm46v/image_processing_pil_glm46v.py
@@ -0,0 +1,263 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/glm46v/modular_glm46v.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_glm46v.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2025 the HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import math
+
+import numpy as np
+
+from ...image_processing_backends import PilBackend
+from ...image_processing_utils import BatchFeature
+from ...image_utils import OPENAI_CLIP_MEAN, OPENAI_CLIP_STD, ImageInput, PILImageResampling, SizeDict
+from ...processing_utils import ImagesKwargs, Unpack
+from ...utils import TensorType, auto_docstring
+
+
+class Glm46VImageProcessorKwargs(ImagesKwargs, total=False):
+ """
+ patch_size (`int`, *optional*, defaults to 14):
+ The spatial patch size of the vision encoder.
+ temporal_patch_size (`int`, *optional*, defaults to 2):
+ The temporal patch size of the vision encoder.
+ merge_size (`int`, *optional*, defaults to 2):
+ The merge size of the vision encoder to llm encoder.
+ """
+
+ patch_size: int
+ temporal_patch_size: int
+ merge_size: int
+
+
+# Adapted from transformers.models.glm46v.image_processing_glm46v.smart_resize
+def smart_resize(
+ num_frames: int,
+ height: int,
+ width: int,
+ temporal_factor: int = 2,
+ factor: int = 28,
+ min_pixels: int = 112 * 112,
+ max_pixels: int = 14 * 14 * 2 * 2 * 2 * 6144,
+):
+ if num_frames < temporal_factor:
+ raise ValueError(f"t:{num_frames} must be larger than temporal_factor:{temporal_factor}")
+ if height < factor or width < factor:
+ scale = max(factor / height, factor / width)
+ height = int(height * scale)
+ width = int(width * scale)
+
+ if max(height, width) / min(height, width) > 200:
+ raise ValueError(
+ f"absolute aspect ratio must be smaller than 200, got {max(height, width) / min(height, width)}"
+ )
+ h_bar = round(height / factor) * factor
+ w_bar = round(width / factor) * factor
+ t_bar = round(num_frames / temporal_factor) * temporal_factor
+
+ if t_bar * h_bar * w_bar > max_pixels:
+ beta = math.sqrt((num_frames * height * width) / max_pixels)
+ h_bar = max(factor, math.floor(height / beta / factor) * factor)
+ w_bar = max(factor, math.floor(width / beta / factor) * factor)
+ elif t_bar * h_bar * w_bar < min_pixels:
+ beta = math.sqrt(min_pixels / (num_frames * height * width))
+ h_bar = math.ceil(height * beta / factor) * factor
+ w_bar = math.ceil(width * beta / factor) * factor
+
+ return h_bar, w_bar
+
+
+@auto_docstring
+class Glm46VImageProcessorPil(PilBackend):
+ do_resize = True
+ resample = PILImageResampling.BICUBIC
+ size = {"shortest_edge": 112 * 112, "longest_edge": 28 * 28 * 15000}
+ default_to_square = False
+ do_rescale = True
+ rescale_factor = 1 / 255
+ do_normalize = True
+ image_mean = OPENAI_CLIP_MEAN
+ image_std = OPENAI_CLIP_STD
+ do_convert_rgb = True
+ patch_size = 14
+ temporal_patch_size = 2
+ merge_size = 2
+ valid_kwargs = Glm46VImageProcessorKwargs
+ model_input_names = ["pixel_values", "image_grid_thw"]
+
+ def __init__(self, **kwargs: Unpack[Glm46VImageProcessorKwargs]):
+ super().__init__(**kwargs)
+ if self.size is not None:
+ if not self.size.shortest_edge or not self.size.longest_edge:
+ raise ValueError("size must contain 'shortest_edge' and 'longest_edge' keys.")
+
+ @auto_docstring
+ def preprocess(self, images: ImageInput, **kwargs: Unpack[Glm46VImageProcessorKwargs]) -> BatchFeature:
+ return super().preprocess(images, **kwargs)
+
+ def _standardize_kwargs(self, **kwargs) -> dict:
+ """
+ Update kwargs that need further processing before being validated
+ Can be overridden by subclasses to customize the processing of kwargs.
+ """
+ kwargs = super()._standardize_kwargs(**kwargs)
+ size = kwargs.get("size", self.size)
+ if not size.shortest_edge or not size.longest_edge:
+ raise ValueError("size must contain 'shortest_edge' and 'longest_edge' keys.")
+
+ return kwargs
+
+ def _preprocess(
+ self,
+ images: list[np.ndarray],
+ do_resize: bool,
+ size: SizeDict,
+ resample: "PILImageResampling | None",
+ do_rescale: bool,
+ rescale_factor: float,
+ do_normalize: bool,
+ image_mean: float | list[float] | None,
+ image_std: float | list[float] | None,
+ patch_size: int,
+ temporal_patch_size: int,
+ merge_size: int,
+ return_tensors: str | TensorType | None,
+ **kwargs,
+ ) -> BatchFeature:
+ """
+ Preprocess images one by one for PIL backend.
+ """
+ processed_images = []
+ processed_grids = []
+
+ for image in images:
+ height, width = image.shape[-2:]
+ if do_resize:
+ resized_height, resized_width = smart_resize(
+ num_frames=temporal_patch_size,
+ height=height,
+ width=width,
+ temporal_factor=temporal_patch_size,
+ factor=patch_size * merge_size,
+ min_pixels=size.shortest_edge,
+ max_pixels=size.longest_edge,
+ )
+ image = self.resize(
+ image,
+ size=SizeDict(height=resized_height, width=resized_width),
+ resample=resample,
+ )
+
+ # Rescale and normalize
+ if do_rescale:
+ image = self.rescale(image, rescale_factor)
+ if do_normalize:
+ image = self.normalize(image, image_mean, image_std)
+
+ # Ensure float32 for patch processing
+ image_array = np.asarray(image, dtype=np.float32)
+ if image_array.ndim == 3: # (C, H, W)
+ image_array = np.expand_dims(image_array, axis=0) # (1, C, H, W)
+ if image_array.ndim == 4: # (B, C, H, W)
+ image_array = np.expand_dims(image_array, axis=1) # (B, T=1, C, H, W)
+
+ resized_height, resized_width = image_array.shape[-2:]
+
+ if image_array.shape[1] % temporal_patch_size != 0:
+ repeats = np.repeat(
+ image_array[:, -1:],
+ temporal_patch_size - (image_array.shape[1] % temporal_patch_size),
+ axis=1,
+ )
+ image_array = np.concatenate([image_array, repeats], axis=1)
+
+ batch_size, t_len, channel = image_array.shape[:3]
+ grid_t = t_len // temporal_patch_size
+ grid_h, grid_w = resized_height // patch_size, resized_width // patch_size
+
+ patches = image_array.reshape(
+ batch_size,
+ grid_t,
+ temporal_patch_size,
+ channel,
+ grid_h // merge_size,
+ merge_size,
+ patch_size,
+ grid_w // merge_size,
+ merge_size,
+ patch_size,
+ )
+ # (B, grid_t, gh, gw, mh, mw, C, tp, ph, pw)
+ patches = np.transpose(patches, (0, 1, 4, 7, 5, 8, 3, 2, 6, 9))
+
+ flatten_patches = patches.reshape(
+ batch_size,
+ grid_t * grid_h * grid_w,
+ channel * temporal_patch_size * patch_size * patch_size,
+ )
+
+ # Remove batch dimension and append: shape is (seq_len, hidden_dim)
+ processed_images.append(flatten_patches.squeeze(0))
+ processed_grids.append([grid_t, grid_h, grid_w])
+
+ # Concatenate all images along sequence dimension: (total_seq_len, hidden_dim)
+ pixel_values = np.concatenate(processed_images, axis=0)
+ image_grid_thw = np.array(processed_grids)
+
+ return BatchFeature(
+ data={"pixel_values": pixel_values, "image_grid_thw": image_grid_thw}, tensor_type=return_tensors
+ )
+
+ def get_number_of_image_patches(self, height: int, width: int, images_kwargs=None):
+ """
+ A utility that returns number of image patches for a given image size.
+
+ Args:
+ height (`int`):
+ Height of the input image.
+ width (`int`):
+ Width of the input image.
+ images_kwargs (`dict`, *optional*)
+ Any kwargs to override defaults of the image processor.
+ Returns:
+ `int`: Number of image patches per image.
+ """
+ if images_kwargs is not None:
+ patch_size = images_kwargs.get("patch_size", self.patch_size)
+ merge_size = images_kwargs.get("merge_size", self.merge_size)
+ size = images_kwargs.get("size", {"shortest_edge": 112 * 112, "longest_edge": 28 * 28 * 15000})
+ else:
+ patch_size = self.patch_size
+ merge_size = self.merge_size
+ size = self.size
+
+ factor = patch_size * merge_size
+ resized_height, resized_width = smart_resize(
+ num_frames=self.temporal_patch_size,
+ height=height,
+ width=width,
+ factor=factor,
+ min_pixels=size["shortest_edge"] if isinstance(size, dict) else size.shortest_edge,
+ max_pixels=size["longest_edge"] if isinstance(size, dict) else size.longest_edge,
+ temporal_factor=self.temporal_patch_size,
+ )
+ grid_h, grid_w = resized_height // patch_size, resized_width // patch_size
+ return grid_h * grid_w
+
+
+__all__ = ["Glm46VImageProcessorPil"]
diff --git a/third_party/transformers/src/transformers/models/glm46v/modeling_glm46v.py b/third_party/transformers/src/transformers/models/glm46v/modeling_glm46v.py
new file mode 100644
index 0000000000000000000000000000000000000000..11e4849405c9fef3ae59bbf6f2d5c9eec9bae8b0
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/glm46v/modeling_glm46v.py
@@ -0,0 +1,885 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/glm46v/modular_glm46v.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_glm46v.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2025 the HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import itertools
+from dataclasses import dataclass
+from typing import Any
+
+import torch
+import torch.nn as nn
+
+from ...cache_utils import Cache
+from ...generation import GenerationMixin
+from ...modeling_outputs import BaseModelOutputWithPooling, ModelOutput
+from ...modeling_utils import PreTrainedModel
+from ...processing_utils import Unpack
+from ...utils import (
+ TransformersKwargs,
+ auto_docstring,
+ can_return_tuple,
+ torch_compilable_check,
+)
+from ..auto import AutoModel
+from .configuration_glm46v import Glm46VConfig
+
+
+@auto_docstring
+class Glm46VPreTrainedModel(PreTrainedModel):
+ config: Glm46VConfig
+ base_model_prefix = "model"
+ input_modalities = ("image", "video", "text")
+ supports_gradient_checkpointing = True
+ _no_split_modules = None
+ _skip_keys_device_placement = "past_key_values"
+ _supports_flash_attn = True
+ _supports_sdpa = True
+
+ _can_compile_fullgraph = True
+ _supports_attention_backend = True
+ _can_record_outputs = None
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ Base class for Llava outputs, with hidden states and attentions.
+ """
+)
+class Glm46VModelOutputWithPast(ModelOutput):
+ r"""
+ past_key_values (`Cache`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
+ It is a [`~cache_utils.Cache`] instance. For more details, see our [kv cache guide](https://huggingface.co/docs/transformers/en/kv_cache).
+
+ Contains pre-computed hidden-states (key and values in the self-attention blocks) that can be used (see
+ `past_key_values` input) to speed up sequential decoding.
+ rope_deltas (`torch.LongTensor` of shape `(batch_size, )`, *optional*):
+ The rope index difference between sequence length and multimodal rope.
+ """
+
+ last_hidden_state: torch.FloatTensor | None = None
+ past_key_values: Cache | None = None
+ hidden_states: tuple[torch.FloatTensor] | None = None
+ attentions: tuple[torch.FloatTensor] | None = None
+ rope_deltas: torch.LongTensor | None = None
+
+
+@auto_docstring
+class Glm46VModel(Glm46VPreTrainedModel):
+ base_model_prefix = "model"
+ # Reference: fix gemma3 grad acc #37208
+ accepts_loss_kwargs = False
+ _no_split_modules = None
+
+ def __init__(self, config):
+ super().__init__(config)
+ self.visual = AutoModel.from_config(config.vision_config)
+ self.language_model = AutoModel.from_config(config.text_config)
+ self.rope_deltas = None # cache rope_deltas here
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ def get_input_embeddings(self):
+ return self.language_model.get_input_embeddings()
+
+ def set_input_embeddings(self, value):
+ self.language_model.set_input_embeddings(value)
+
+ def get_vision_position_ids(
+ self,
+ start_position: int,
+ grid_thw: list[int, int, int] | torch.Tensor,
+ temp_merge_size: int = 1,
+ spatial_merge_size: int = 1,
+ time_interval: int = 1,
+ device: str | torch.device | None = None,
+ ):
+ """
+ Compute 3D positional indices for vision tokens derived from a single image or video input.
+
+ The positions are generated from the input grid defined by temporal (T), height (H), and
+ width (W) dimensions. Temporal and spatial dimensions can be downscaled according to the
+ merge sizes used in the vision backbone. The resulting positions are offset by `start_position`.
+
+ Args:
+ start_position (`int`):
+ Offset added to all computed positional indices.
+ grid_thw (`Sequence[int]` or `torch.Tensor` of shape `(3,)`):
+ The (T, H, W) grid representing the feature layout of the current image or video after patch embedding.
+ temp_merge_size (`int`, *optional*):
+ Factor by which the temporal dimension is reduced in the backbone. The temporal grid size is divided
+ by this value. Defaults to 1.
+ spatial_merge_size (`int`, *optional*):
+ Factor by which the spatial dimensions (H and W) are reduced in the backbone. Both H and W are divided
+ by this value. Defaults to 1.
+ time_interval (`int`, *optional*):
+ Spacing factor applied between consecutive temporal position indices.Defaults to 1.
+ device (`str` or `torch.device`, *optional*):
+ Device on which the resulting tensor is allocated. If `None`, uses the current default device.
+
+ Returns:
+ torch.LongTensor of shape (3, sequence_length):
+ Positional indices for temporal, height, and width dimensions,
+ flattened into sequence form and offset by `start_position`.
+ """
+ llm_grid_t, llm_grid_h, llm_grid_w = (
+ grid_thw[0].item() // temp_merge_size,
+ grid_thw[1].item() // spatial_merge_size,
+ grid_thw[2].item() // spatial_merge_size,
+ )
+
+ image_seq_length = llm_grid_h * llm_grid_w * llm_grid_t
+ position_width = torch.arange(start_position, start_position + llm_grid_w, device=device).repeat(
+ llm_grid_h * llm_grid_t
+ )
+ position_height = torch.arange(start_position, start_position + llm_grid_h, device=device).repeat_interleave(
+ llm_grid_w * llm_grid_t
+ )
+ position_temporal = torch.full((image_seq_length,), start_position, device=device, dtype=torch.long)
+ position_temporal = position_temporal * time_interval
+ vision_position_ids = torch.stack([position_temporal, position_height, position_width], dim=0)
+
+ return vision_position_ids
+
+ def get_rope_index(
+ self,
+ input_ids: torch.LongTensor,
+ mm_token_type_ids: torch.IntTensor,
+ image_grid_thw: torch.LongTensor | None = None,
+ video_grid_thw: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ **kwargs,
+ ) -> tuple[torch.Tensor, torch.Tensor]:
+ """
+ Calculate the 3D rope index based on image and video's sizes. The utility expects a `vision + text`
+ sequence and will error out otherwise. For pure text sequence, please rely on model's auto-inferred
+ position ids. In a mixed vision + text sequence, vision tokens use 3D RoPE (temporal, height, width)
+ while text tokens use standard 1D RoPE.
+
+ Example:
+ Temporal patches: 3; Height patches: 2; Width patches: 2
+ Each vision input results in (temporal x height × width) positions. Here: 3 x 2 × 2 = 12 positions total.
+
+ Temporal position IDs are spaced by:
+ `interval = tokens_per_second * temporal_patch_size / fps`
+
+ If fps = 1; tokens_per_second = 25; temporal_patch_size = 2, temporal IDs increase by 50 for each temporal patch:
+ `[0, 0, 0, 0, 50, 50, 50, 50, 100, 100, 100, 100]`
+
+ Height IDs repeat per row: `[0, 0, 1, 1, ...]`
+ Width IDs alternate per column: `[0, 1, 0, 1, ...]`
+ Text tokens follow standard 1D RoPE and the position IDs grow consequently with a step of `1`
+
+ Args:
+ input_ids (`torch.LongTensor` of shape `(batch_size, sequence_length)`):
+ Indices of input sequence tokens in the vocabulary. Padding will be ignored by default should you provide
+ it.
+ mm_token_type_ids (`torch.IntTensor` of shape `(batch_size, sequence_length)`):
+ Token type ids matching each modality to a different value in the input sequence, i.e. text (0), image (1), video (2).
+ 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.
+ attention_mask (`torch.Tensor` of shape `(batch_size, sequence_length)`, *optional*):
+ Mask to avoid performing attention on padding token indices. Mask values selected in `[0, 1]`:
+
+ - 1 for tokens that are **not masked**,
+ - 0 for tokens that are **masked**.
+
+ Returns:
+ position_ids (`torch.LongTensor` of shape `(3, batch_size, sequence_length)`)
+ mrope_position_deltas (`torch.Tensor` of shape `(batch_size)`)
+ """
+ spatial_merge_size = self.config.vision_config.spatial_merge_size
+
+ mrope_position_deltas = []
+ position_ids = torch.zeros(
+ 3,
+ input_ids.shape[0],
+ input_ids.shape[1],
+ dtype=input_ids.dtype,
+ device=input_ids.device,
+ )
+ grid_iters = {
+ 1: iter(image_grid_thw) if image_grid_thw is not None else None,
+ 2: iter(video_grid_thw) if video_grid_thw is not None else None,
+ }
+
+ for batch_idx, current_input_ids in enumerate(input_ids):
+ input_token_type = mm_token_type_ids[batch_idx]
+ if attention_mask is not None:
+ current_input_ids = current_input_ids[attention_mask[batch_idx].bool()]
+ input_token_type = input_token_type[attention_mask[batch_idx].bool()]
+
+ input_type_group = []
+ for key, group in itertools.groupby(enumerate(input_token_type.tolist()), lambda x: x[1]):
+ group = list(group)
+ start_index = group[0][0]
+ end_index = group[-1][0] + 1
+ input_type_group.append((key, start_index, end_index))
+
+ current_pos = 0
+ video_group_index = 0
+ llm_pos_ids_list = []
+ for modality_type, start_idx, end_idx in input_type_group:
+ # text == 0
+ if modality_type == 0:
+ text_len = end_idx - start_idx
+ llm_pos_ids_list.append(
+ torch.arange(text_len, device=input_ids.device).view(1, -1).expand(3, -1) + current_pos
+ )
+ current_pos += text_len
+ # image == 1, video == 2
+ else:
+ # GLM46V splits video into segments per frame but there's only one `grid_thw`
+ # per whole video. We can't exhaus the iterator and have to re-use the grid
+ # while processing the same video!
+ if modality_type == 2:
+ if video_group_index == 0:
+ grid_thw = next(grid_iters[modality_type])
+ video_group_index += 1
+ video_group_index = 0 if video_group_index >= grid_thw[0] else video_group_index
+ else:
+ grid_thw = next(grid_iters[modality_type])
+
+ # Videos are processed per frame separately, each temporal grid is always `1`
+ temp_merge_size = grid_thw[0]
+ vision_position_ids = self.get_vision_position_ids(
+ current_pos, grid_thw, temp_merge_size, spatial_merge_size, device=input_ids.device
+ )
+ llm_pos_ids_list.append(vision_position_ids)
+ current_pos += max(grid_thw[1], grid_thw[2]) // spatial_merge_size
+ llm_positions = torch.cat(llm_pos_ids_list, dim=1).reshape(3, -1)
+ if attention_mask is not None:
+ position_ids[:, batch_idx, attention_mask[batch_idx].bool()] = llm_positions.to(position_ids.device)
+ else:
+ position_ids[:, batch_idx] = llm_positions.to(position_ids.device)
+ mrope_position_deltas.append(llm_positions.max() + 1 - len(current_input_ids))
+ mrope_position_deltas = torch.tensor(mrope_position_deltas, device=input_ids.device).unsqueeze(1)
+ return position_ids, mrope_position_deltas
+
+ @can_return_tuple
+ @auto_docstring
+ def get_video_features(
+ self,
+ pixel_values_videos: torch.FloatTensor,
+ video_grid_thw: torch.LongTensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | BaseModelOutputWithPooling:
+ r"""
+ pixel_values_videos (`torch.FloatTensor` of shape `(batch_size, num_channels, image_size, image_size)`):
+ The tensors corresponding to the input videos.
+ video_grid_thw (`torch.LongTensor` of shape `(num_videos, 3)`, *optional*):
+ The temporal, height and width of feature shape of each video in LLM.
+ """
+ pixel_values_videos = pixel_values_videos.type(self.visual.dtype)
+ # reshape video_grid_thw -> [b, 3] -> [1, h, w] * frames
+ temp_frames_hw = []
+ video_grid_thw_list = video_grid_thw.tolist()
+ for t, h, w in video_grid_thw_list:
+ repeated_row = torch.tensor([1, h, w]).unsqueeze(0).repeat(t, 1)
+ temp_frames_hw.append(repeated_row)
+ flattened_video_grid_thw = torch.cat(temp_frames_hw, dim=0)
+ vision_outputs = self.visual(
+ pixel_values_videos, grid_thw=flattened_video_grid_thw, return_dict=True, **kwargs
+ )
+ split_sizes = (video_grid_thw.prod(-1) // self.visual.spatial_merge_size**2).tolist()
+ video_embeds = torch.split(vision_outputs.pooler_output, split_sizes)
+ vision_outputs.pooler_output = video_embeds
+
+ return vision_outputs
+
+ @can_return_tuple
+ @auto_docstring
+ def get_image_features(
+ self,
+ pixel_values: torch.FloatTensor,
+ image_grid_thw: torch.LongTensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | BaseModelOutputWithPooling:
+ r"""
+ pixel_values (`torch.FloatTensor` of shape `(batch_size, num_channels, image_size, image_size)`):
+ The tensors corresponding to the input images.
+ image_grid_thw (`torch.LongTensor` of shape `(num_images, 3)`, *optional*):
+ The temporal, height and width of feature shape of each image in LLM.
+ """
+ pixel_values = pixel_values.type(self.visual.dtype)
+ vision_outputs = self.visual(pixel_values, grid_thw=image_grid_thw, **kwargs)
+ split_sizes = (image_grid_thw.prod(-1) // self.visual.spatial_merge_size**2).tolist()
+ image_embeds = torch.split(vision_outputs.pooler_output, split_sizes)
+ vision_outputs.pooler_output = image_embeds
+
+ return vision_outputs
+
+ def get_placeholder_mask(
+ self,
+ input_ids: torch.LongTensor,
+ inputs_embeds: torch.FloatTensor,
+ image_features: torch.FloatTensor | None = None,
+ video_features: torch.FloatTensor | None = None,
+ ):
+ """
+ Obtains multimodal placeholder mask from `input_ids` or `inputs_embeds`, and checks that the placeholder token count is
+ equal to the length of multimodal features. If the lengths are different, an error is raised.
+ """
+ if input_ids is None:
+ special_image_mask = inputs_embeds == self.get_input_embeddings()(
+ torch.tensor(self.config.image_token_id, dtype=torch.long, device=inputs_embeds.device)
+ )
+ special_image_mask = special_image_mask.all(-1)
+ special_video_mask = inputs_embeds == self.get_input_embeddings()(
+ torch.tensor(self.config.video_token_id, dtype=torch.long, device=inputs_embeds.device)
+ )
+ special_video_mask = special_video_mask.all(-1)
+ else:
+ # GLM-4.1V and GLM-4.5V special_video_mask is special_image_mask
+ special_image_mask = input_ids == self.config.image_token_id
+ special_video_mask = input_ids == self.config.image_token_id
+
+ n_image_tokens = special_image_mask.sum()
+ special_image_mask = special_image_mask.unsqueeze(-1).expand_as(inputs_embeds).to(inputs_embeds.device)
+ if image_features is not None:
+ torch_compilable_check(
+ inputs_embeds[special_image_mask].numel() == image_features.numel(),
+ f"Image features and image tokens do not match, tokens: {n_image_tokens}, features: {image_features.shape[0]}",
+ )
+
+ n_video_tokens = special_video_mask.sum()
+ special_video_mask = special_video_mask.unsqueeze(-1).expand_as(inputs_embeds).to(inputs_embeds.device)
+ if video_features is not None:
+ torch_compilable_check(
+ inputs_embeds[special_video_mask].numel() == video_features.numel(),
+ f"Video features and video tokens do not match, tokens: {n_video_tokens}, features: {video_features.shape[0]}",
+ )
+ return special_image_mask, special_video_mask
+
+ def compute_3d_position_ids(
+ self,
+ input_ids: torch.Tensor | None,
+ inputs_embeds: torch.Tensor | None,
+ image_grid_thw: torch.Tensor | None = None,
+ video_grid_thw: torch.Tensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ past_key_values: torch.Tensor | None = None,
+ mm_token_type_ids: torch.IntTensor | None = None,
+ ) -> torch.Tensor | None:
+ past_key_values_length = 0 if past_key_values is None else past_key_values.get_seq_length()
+ has_multimodal = image_grid_thw is not None or video_grid_thw is not None
+ if has_multimodal and mm_token_type_ids is None and input_ids is not None:
+ raise ValueError(
+ "Multimodal data was passed (via `image_grid_thw` or `video_grid_thw`) but `mm_token_type_ids` is "
+ "missing. Please pass `mm_token_type_ids` to the model so that multimodal RoPE (M-RoPE) can be "
+ "computed correctly. `mm_token_type_ids` is returned by the processor alongside `input_ids`."
+ )
+ can_compute_mrope = input_ids is not None and mm_token_type_ids is not None and has_multimodal
+
+ if can_compute_mrope and (self.rope_deltas is None or past_key_values_length == 0):
+ position_ids, rope_deltas = self.get_rope_index(
+ input_ids,
+ image_grid_thw=image_grid_thw,
+ video_grid_thw=video_grid_thw,
+ attention_mask=attention_mask,
+ mm_token_type_ids=mm_token_type_ids,
+ )
+ self.rope_deltas = rope_deltas
+ # Use pre-calculated rope-deltas to infer correct 3D position ids during incremental
+ # generation (past_key_values_length > 0) or when only inputs_embeds is provided (no input_ids
+ # to recompute from). Skip when input_ids is provided without past_key_values to avoid shape
+ # mismatches from stale rope_deltas (e.g., training forward pass after generation).
+ elif self.rope_deltas is not None and (past_key_values_length > 0 or input_ids is None):
+ batch_size, seq_length, _ = inputs_embeds.shape
+ if attention_mask is not None:
+ position_ids = attention_mask.long().cumsum(-1) - 1
+ position_ids = position_ids.masked_fill(attention_mask == 0, 0)
+ position_ids = position_ids.view(1, batch_size, -1).repeat(3, 1, 1).to(inputs_embeds.device)
+ else:
+ position_ids = torch.arange(past_key_values_length, past_key_values_length + seq_length)
+ position_ids = position_ids.view(1, 1, -1).expand(3, batch_size, -1).to(inputs_embeds.device)
+ delta = self.rope_deltas.repeat_interleave(batch_size // self.rope_deltas.shape[0], dim=0)
+ position_ids = position_ids + delta.to(device=inputs_embeds.device)
+ else:
+ # Can't build correct 3D positions. Let the model infer it
+ position_ids = None
+ return position_ids
+
+ @auto_docstring
+ @can_return_tuple
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ pixel_values: torch.Tensor | None = None,
+ pixel_values_videos: torch.FloatTensor | None = None,
+ image_grid_thw: torch.LongTensor | None = None,
+ video_grid_thw: torch.LongTensor | None = None,
+ rope_deltas: torch.LongTensor | None = None,
+ mm_token_type_ids: torch.IntTensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | Glm46VModelOutputWithPast:
+ r"""
+ 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.
+ """
+ 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 None:
+ inputs_embeds = self.get_input_embeddings()(input_ids)
+
+ if pixel_values is not None:
+ image_embeds = self.get_image_features(pixel_values, image_grid_thw, return_dict=True).pooler_output
+ image_embeds = torch.cat(image_embeds, dim=0).to(inputs_embeds.device, inputs_embeds.dtype)
+ image_mask, _ = self.get_placeholder_mask(input_ids, inputs_embeds, image_features=image_embeds)
+ inputs_embeds = inputs_embeds.masked_scatter(image_mask, image_embeds)
+
+ if pixel_values_videos is not None:
+ video_embeds = self.get_video_features(pixel_values_videos, video_grid_thw, return_dict=True).pooler_output
+ video_embeds = torch.cat(video_embeds, dim=0).to(inputs_embeds.device, inputs_embeds.dtype)
+ _, video_mask = self.get_placeholder_mask(input_ids, inputs_embeds, video_features=video_embeds)
+ inputs_embeds = inputs_embeds.masked_scatter(video_mask, video_embeds)
+
+ if position_ids is None:
+ position_ids = self.compute_3d_position_ids(
+ input_ids=input_ids,
+ image_grid_thw=image_grid_thw,
+ video_grid_thw=video_grid_thw,
+ inputs_embeds=inputs_embeds,
+ attention_mask=attention_mask,
+ past_key_values=past_key_values,
+ mm_token_type_ids=mm_token_type_ids,
+ )
+
+ outputs = self.language_model(
+ input_ids=None,
+ position_ids=position_ids,
+ attention_mask=attention_mask,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ **kwargs,
+ )
+
+ return Glm46VModelOutputWithPast(
+ **outputs,
+ rope_deltas=self.rope_deltas,
+ )
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ Base class for Glm46V causal language model (or autoregressive) outputs.
+ """
+)
+class Glm46VCausalLMOutputWithPast(ModelOutput):
+ r"""
+ loss (`torch.FloatTensor` of shape `(1,)`, *optional*, returned when `labels` is provided):
+ Language modeling loss (for next-token prediction).
+ logits (`torch.FloatTensor` of shape `(batch_size, sequence_length, config.vocab_size)`):
+ Prediction scores of the language modeling head (scores for each vocabulary token before SoftMax).
+ past_key_values (`Cache`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
+ It is a [`~cache_utils.Cache`] instance. For more details, see our [kv cache guide](https://huggingface.co/docs/transformers/en/kv_cache).
+
+ Contains pre-computed hidden-states (key and values in the self-attention blocks) that can be used (see
+ `past_key_values` input) to speed up sequential decoding.
+ rope_deltas (`torch.LongTensor` of shape `(batch_size, )`, *optional*):
+ The rope index difference between sequence length and multimodal rope.
+ """
+
+ loss: torch.FloatTensor | None = None
+ logits: torch.FloatTensor | None = None
+ past_key_values: Cache | None = None
+ hidden_states: tuple[torch.FloatTensor] | None = None
+ attentions: tuple[torch.FloatTensor] | None = None
+ rope_deltas: torch.LongTensor | None = None
+
+
+class Glm46VForConditionalGeneration(Glm46VPreTrainedModel, GenerationMixin):
+ _tied_weights_keys = {"lm_head.weight": "model.language_model.embed_tokens.weight"}
+ # Reference: fix gemma3 grad acc #37208
+ accepts_loss_kwargs = False
+
+ def __init__(self, config):
+ super().__init__(config)
+ self.model = Glm46VModel(config)
+ self.lm_head = nn.Linear(config.text_config.hidden_size, config.text_config.vocab_size, bias=False)
+
+ self.post_init()
+
+ def get_input_embeddings(self):
+ return self.model.get_input_embeddings()
+
+ def set_input_embeddings(self, value):
+ self.model.set_input_embeddings(value)
+
+ @auto_docstring
+ def get_video_features(
+ self,
+ pixel_values_videos: torch.FloatTensor,
+ video_grid_thw: torch.LongTensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | BaseModelOutputWithPooling:
+ r"""
+ pixel_values_videos (`torch.FloatTensor` of shape `(batch_size, num_channels, image_size, image_size)`):
+ The tensors corresponding to the input videos.
+ video_grid_thw (`torch.LongTensor` of shape `(num_videos, 3)`, *optional*):
+ The temporal, height and width of feature shape of each video in LLM.
+ """
+ return self.model.get_video_features(
+ pixel_values_videos=pixel_values_videos, video_grid_thw=video_grid_thw, **kwargs
+ )
+
+ @auto_docstring
+ def get_image_features(
+ self,
+ pixel_values: torch.FloatTensor,
+ image_grid_thw: torch.LongTensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | BaseModelOutputWithPooling:
+ r"""
+ pixel_values (`torch.FloatTensor` of shape `(batch_size, num_channels, image_size, image_size)`):
+ The tensors corresponding to the input images.
+ image_grid_thw (`torch.LongTensor` of shape `(num_images, 3)`, *optional*):
+ The temporal, height and width of feature shape of each image in LLM.
+ """
+ return self.model.get_image_features(pixel_values=pixel_values, image_grid_thw=image_grid_thw, **kwargs)
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ pixel_values: torch.Tensor | None = None,
+ pixel_values_videos: torch.FloatTensor | None = None,
+ image_grid_thw: torch.LongTensor | None = None,
+ video_grid_thw: torch.LongTensor | None = None,
+ mm_token_type_ids: torch.IntTensor | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | Glm46VCausalLMOutputWithPast:
+ r"""
+ 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]`.
+ 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.
+
+ Example:
+
+ ```python
+ >>> from PIL import Image
+ >>> import httpx
+ >>> from io import BytesIO
+ >>> from transformers import AutoProcessor, Glm46VForConditionalGeneration
+
+ >>> model = Glm46VForConditionalGeneration.from_pretrained("zai-org/GLM-4.1V-9B-Thinking")
+ >>> processor = AutoProcessor.from_pretrained("zai-org/GLM-4.1V-9B-Thinking")
+
+ >>> messages = [
+ {
+ "role": "user",
+ "content": [
+ {"type": "image", "url": "https://www.ilankelman.org/stopsigns/australia.jpg"},
+ {"type": "text", "text": "What is shown in this image?"},
+ ],
+ },
+ ]
+ >>> url = "https://www.ilankelman.org/stopsigns/australia.jpg"
+ >>> with httpx.stream("GET", url) as response:
+ ... image = Image.open(BytesIO(response.read()))
+
+ >>> text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
+ >>> inputs = processor(text=[text], images=[image], vision_infos=[vision_infos])
+
+ >>> # Generate
+ >>> generate_ids = model.generate(inputs.input_ids, max_length=30)
+ >>> 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 ..."
+ ```"""
+ outputs = self.model(
+ 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,
+ mm_token_type_ids=mm_token_type_ids,
+ position_ids=position_ids,
+ attention_mask=attention_mask,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ **kwargs,
+ )
+
+ hidden_states = outputs[0]
+
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(logits=logits, labels=labels, vocab_size=self.config.text_config.vocab_size)
+
+ return Glm46VCausalLMOutputWithPast(
+ loss=loss,
+ logits=logits,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ rope_deltas=outputs.rope_deltas,
+ )
+
+ def prepare_inputs_for_generation(
+ self,
+ input_ids,
+ past_key_values=None,
+ attention_mask=None,
+ inputs_embeds=None,
+ position_ids=None,
+ use_cache=True,
+ pixel_values=None,
+ pixel_values_videos=None,
+ image_grid_thw=None,
+ video_grid_thw=None,
+ is_first_iteration=False,
+ **kwargs,
+ ):
+ # Overwritten -- in specific circumstances we don't want to forward image inputs to the model
+
+ model_inputs = super().prepare_inputs_for_generation(
+ input_ids,
+ past_key_values=past_key_values,
+ attention_mask=attention_mask,
+ inputs_embeds=inputs_embeds,
+ position_ids=position_ids,
+ pixel_values=pixel_values,
+ pixel_values_videos=pixel_values_videos,
+ image_grid_thw=image_grid_thw,
+ video_grid_thw=video_grid_thw,
+ use_cache=use_cache,
+ is_first_iteration=is_first_iteration,
+ **kwargs,
+ )
+
+ if not is_first_iteration and use_cache:
+ model_inputs["pixel_values"] = None
+ model_inputs["pixel_values_videos"] = None
+
+ return model_inputs
+
+ def _prepare_position_ids_for_generation(self, inputs_tensor, model_kwargs):
+ # Overwritten -- requires 3D position ids
+
+ text_positions = super()._prepare_position_ids_for_generation(inputs_tensor, model_kwargs)
+
+ # Early exit in case we are continuing generation from past kv
+ past_length = 0
+ if (cache := model_kwargs.get("past_key_values")) is not None:
+ past_length = cache.get_seq_length()
+ if past_length != 0 and self.model.rope_deltas is not None:
+ position_ids = text_positions[None, ...] + self.model.rope_deltas
+ return position_ids
+
+ # Otherwise compute 3d position ids for vision tokens and concat with text position ids
+ if "input_ids" in model_kwargs and model_kwargs["input_ids"].shape[1] > 0:
+ inputs_tensor = model_kwargs["input_ids"]
+
+ is_input_ids = len(inputs_tensor.shape) == 2 and inputs_tensor.dtype in [torch.int, torch.long]
+ if (
+ is_input_ids
+ and model_kwargs.get("mm_token_type_ids") is not None
+ and (model_kwargs.get("image_grid_thw") is not None or model_kwargs.get("video_grid_thw") is not None)
+ ):
+ model_kwargs = {k: v for k, v in model_kwargs.items() if k != "input_ids"}
+ vision_positions, rope_deltas = self.model.get_rope_index(inputs_tensor, **model_kwargs)
+ self.model.rope_deltas = rope_deltas
+ else:
+ vision_positions = text_positions.unsqueeze(0).expand(3, -1, -1)
+ self.model.rope_deltas = torch.zeros(
+ inputs_tensor.shape[0], 1, dtype=torch.long, device=inputs_tensor.device
+ )
+
+ # Concatenate "text + vision" positions into [4, bs, seq-len]
+ text_positions = text_positions[None, ...]
+ position_ids = torch.cat([text_positions, vision_positions], dim=0)
+
+ return position_ids
+
+ def _get_image_nums_and_video_nums(
+ self,
+ input_ids: torch.LongTensor | None,
+ inputs_embeds: torch.Tensor | None = None,
+ ) -> tuple[torch.Tensor, torch.Tensor]:
+ """
+ Get the number of images and videos for each sample to calculate the separation length of the sample tensor.
+ These parameters are not passed through the processor to avoid unpredictable impacts from interface modifications.
+
+ Args:
+ input_ids (`torch.LongTensor` of shape `(batch_size, sequence_length)`):
+ Indices of input sequence tokens in the vocabulary.
+
+ Returns:
+ image_nums (`torch.LongTensor` of shape `(batch_size, num_images_sample)`)
+ video_nums (`torch.LongTensor` of shape `(batch_size, num_videos_sample)`)
+ """
+
+ if inputs_embeds is not None:
+ is_image = (
+ inputs_embeds
+ == self.get_input_embeddings()(
+ torch.tensor(self.config.image_start_token_id, dtype=torch.long, device=inputs_embeds.device)
+ )
+ )[..., 0]
+ is_video_start = (
+ inputs_embeds
+ == self.get_input_embeddings()(
+ torch.tensor(self.config.video_start_token_id, dtype=torch.long, device=inputs_embeds.device)
+ )
+ )[..., 0]
+ is_video_end = (
+ inputs_embeds
+ == self.get_input_embeddings()(
+ torch.tensor(self.config.video_end_token_id, dtype=torch.long, device=inputs_embeds.device)
+ )
+ )[..., 0]
+ else:
+ is_image = input_ids == self.config.image_start_token_id
+ is_video_start = input_ids == self.config.video_start_token_id
+ is_video_end = input_ids == self.config.video_end_token_id
+
+ # Cumulative sum to track if we're inside a video span
+ # We'll assume well-formed video tags (i.e. matching starts and ends)
+ video_level = torch.cumsum(is_video_start.int() - is_video_end.int(), dim=1)
+ inside_video = video_level > 0 # shape (batch_size, seq_length)
+
+ # Mask out image tokens that are inside video spans
+ standalone_images = is_image & (~inside_video)
+
+ # Count per batch
+ image_counts = standalone_images.sum(dim=1)
+ video_counts = is_video_start.sum(dim=1)
+
+ return image_counts, video_counts
+
+ def _expand_inputs_for_generation(
+ self,
+ expand_size: int = 1,
+ is_encoder_decoder: bool = False,
+ input_ids: torch.LongTensor | None = None,
+ **model_kwargs,
+ ) -> tuple[torch.LongTensor, dict[str, Any]]:
+ # Overwritten -- Support for expanding tensors without a batch size dimension
+ # e.g., pixel_values, image_grid_thw, pixel_values_videos, video_grid_thw, second_per_grid_t
+ # pixel_values.shape[0] is sum(seqlen_images for samples)
+ # image_grid_thw.shape[0] is sum(num_images for samples)
+
+ if expand_size == 1:
+ return input_ids, model_kwargs
+
+ visual_keys = ["pixel_values", "image_grid_thw", "pixel_values_videos", "video_grid_thw", "second_per_grid_ts"]
+
+ def _expand_dict_for_generation_visual(dict_to_expand):
+ image_grid_thw = model_kwargs.get("image_grid_thw", None)
+ video_grid_thw = model_kwargs.get("video_grid_thw", None)
+ image_nums, video_nums = self._get_image_nums_and_video_nums(
+ input_ids, inputs_embeds=model_kwargs.get("inputs_embeds", None)
+ )
+
+ def _repeat_interleave_samples(x, lengths, repeat_times):
+ samples = torch.split(x, lengths)
+ repeat_args = [repeat_times] + [1] * (x.dim() - 1)
+ result = torch.cat([sample.repeat(*repeat_args) for sample in samples], dim=0)
+ return result
+
+ for key in dict_to_expand:
+ if key == "pixel_values":
+ # split images into samples
+ samples = torch.split(image_grid_thw, list(image_nums))
+ # compute the sequence length of images for each sample
+ lengths = [torch.prod(sample, dim=1).sum() for sample in samples]
+ dict_to_expand[key] = _repeat_interleave_samples(
+ dict_to_expand[key], lengths=lengths, repeat_times=expand_size
+ )
+ elif key == "image_grid_thw":
+ # get the num of images for each sample
+ lengths = list(image_nums)
+ dict_to_expand[key] = _repeat_interleave_samples(
+ dict_to_expand[key], lengths=lengths, repeat_times=expand_size
+ )
+ elif key == "pixel_values_videos":
+ samples = torch.split(video_grid_thw, list(video_nums))
+ lengths = [torch.prod(sample, dim=1).sum() for sample in samples]
+ dict_to_expand[key] = _repeat_interleave_samples(
+ dict_to_expand[key], lengths=lengths, repeat_times=expand_size
+ )
+ elif key == "video_grid_thw":
+ lengths = list(video_nums)
+ dict_to_expand[key] = _repeat_interleave_samples(
+ dict_to_expand[key], lengths=lengths, repeat_times=expand_size
+ )
+ elif key == "second_per_grid_ts":
+ dict_to_expand[key] = _repeat_interleave_samples(
+ dict_to_expand[key], lengths=list(video_nums), repeat_times=expand_size
+ )
+ return dict_to_expand
+
+ def _expand_dict_for_generation(dict_to_expand):
+ for key in dict_to_expand:
+ if key == "position_ids" and dict_to_expand[key].ndim == 3:
+ dict_to_expand[key] = dict_to_expand[key].repeat_interleave(expand_size, dim=1)
+ elif (
+ dict_to_expand[key] is not None
+ and isinstance(dict_to_expand[key], torch.Tensor)
+ and key not in visual_keys
+ ):
+ dict_to_expand[key] = dict_to_expand[key].repeat_interleave(expand_size, dim=0)
+ return dict_to_expand
+
+ model_kwargs = _expand_dict_for_generation_visual(model_kwargs)
+
+ if input_ids is not None:
+ input_ids = input_ids.repeat_interleave(expand_size, dim=0)
+
+ model_kwargs = _expand_dict_for_generation(model_kwargs)
+
+ if is_encoder_decoder:
+ if model_kwargs.get("encoder_outputs") is None:
+ raise ValueError("If `is_encoder_decoder` is True, make sure that `encoder_outputs` is defined.")
+ model_kwargs["encoder_outputs"] = _expand_dict_for_generation(model_kwargs["encoder_outputs"])
+
+ return input_ids, model_kwargs
+
+
+__all__ = ["Glm46VModel", "Glm46VPreTrainedModel", "Glm46VForConditionalGeneration"]
diff --git a/third_party/transformers/src/transformers/models/glm46v/modular_glm46v.py b/third_party/transformers/src/transformers/models/glm46v/modular_glm46v.py
new file mode 100644
index 0000000000000000000000000000000000000000..0fdcef45136f8f18f10403e0582ca4c3bd0dd722
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/glm46v/modular_glm46v.py
@@ -0,0 +1,197 @@
+# Copyright 2025 the HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import numpy as np
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...utils import auto_docstring
+from ...video_utils import VideoMetadata
+from ..auto import CONFIG_MAPPING, AutoConfig, AutoModel
+from ..glm4v.image_processing_glm4v import Glm4vImageProcessor
+from ..glm4v.image_processing_pil_glm4v import Glm4vImageProcessorPil
+from ..glm4v.modeling_glm4v import Glm4vForConditionalGeneration, Glm4vModel, Glm4vPreTrainedModel
+from ..glm4v.processing_glm4v import Glm4vProcessor
+from ..glm4v.video_processing_glm4v import Glm4vVideoProcessor
+
+
+@auto_docstring(checkpoint="zai-org/GLM-4.1V-9B-Thinking")
+@strict
+class Glm46VConfig(PreTrainedConfig):
+ r"""
+ image_start_token_id (`int`, *optional*, defaults to 151339):
+ The image start token index to encode the start of image.
+ image_end_token_id (`int`, *optional*, defaults to 151340):
+ The image end token index to encode the end of image.
+ video_start_token_id (`int`, *optional*, defaults to 151361):
+ The video start token index to encode the start of video.
+ video_end_token_id (`int`, *optional*, defaults to 151362):
+ The video end token index to encode the end of video.
+
+ ```python
+ >>> from transformers import Glm46VForConditionalGeneration, Glm46VConfig
+
+ >>> # Initializing a GLM-4.6V style configuration
+ >>> configuration = Glm46VConfig()
+
+ >>> # Initializing a model from the GLM-4.6V style configuration
+ >>> model = Glm4vForConditionalGeneration(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "glm46v"
+ sub_configs = {"text_config": AutoConfig, "vision_config": AutoConfig}
+ keys_to_ignore_at_inference = ["past_key_values"]
+
+ text_config: dict | PreTrainedConfig | None = None
+ vision_config: dict | PreTrainedConfig | None = None
+ image_token_id: int = 151343
+ video_token_id: int = 151344
+ image_start_token_id: int = 151339
+ image_end_token_id: int = 151340
+ video_start_token_id: int = 151361
+ video_end_token_id: int = 151362
+ tie_word_embeddings: bool = False
+
+ def __post_init__(self, **kwargs):
+ if isinstance(self.vision_config, dict):
+ self.vision_config["model_type"] = self.vision_config.get("model_type", "glm4v_vision")
+ self.vision_config = CONFIG_MAPPING[self.vision_config["model_type"]](**self.vision_config)
+ elif self.vision_config is None:
+ self.vision_config = CONFIG_MAPPING["glm4v_vision"]()
+
+ if isinstance(self.text_config, dict):
+ self.text_config["model_type"] = self.text_config.get("model_type", "glm4v_text")
+ self.text_config = CONFIG_MAPPING[self.text_config["model_type"]](**self.text_config)
+ elif self.text_config is None:
+ self.text_config = CONFIG_MAPPING["glm4v_text"]()
+
+ super().__post_init__(**kwargs)
+
+
+class Glm46VPreTrainedModel(Glm4vPreTrainedModel):
+ _can_record_outputs = None
+ _no_split_modules = None
+
+ def _init_weights(self, module):
+ raise AttributeError("Not needed")
+
+
+class Glm46VModel(Glm4vModel):
+ _no_split_modules = None
+
+ def __init__(self, config):
+ super().__init__(config)
+ self.visual = AutoModel.from_config(config.vision_config)
+ self.language_model = AutoModel.from_config(config.text_config)
+
+
+class Glm46VForConditionalGeneration(Glm4vForConditionalGeneration):
+ pass
+
+
+class Glm46VProcessor(Glm4vProcessor):
+ def replace_frame_token_id(self, timestamp_sec):
+ return f"<|begin_of_image|>{self.image_token}<|end_of_image|>{timestamp_sec:.1f} seconds"
+
+
+class Glm46VImageProcessorPil(Glm4vImageProcessorPil):
+ pass
+
+
+class Glm46VImageProcessor(Glm4vImageProcessor):
+ pass
+
+
+class Glm46VVideoProcessor(Glm4vVideoProcessor):
+ def sample_frames(
+ self,
+ metadata: VideoMetadata,
+ fps: int | float | None = None,
+ **kwargs,
+ ):
+ if metadata is None or getattr(metadata, "fps", None) is None:
+ raise ValueError(
+ "Asked to sample frames per second but no video metadata was provided which is required when sampling in Glm46V. "
+ "Please pass in `VideoMetadata` object or set `do_sample_frames=False`"
+ )
+
+ total_frames = metadata.total_num_frames
+ max_frame_idx = total_frames - 1
+ duration = metadata.duration or round(max_frame_idx / metadata.fps) + 1
+
+ DYNAMIC_FPS_THRES = {30: 3, 300: 1, 2400: 0.5}
+ MAX_FRAME_COUNT_DYNAMIC = 640
+ MAX_DURATION = 2400
+ effective_duration = min(duration, MAX_DURATION)
+ if effective_duration <= 30:
+ target_fps = DYNAMIC_FPS_THRES[30]
+ elif effective_duration <= 300:
+ target_fps = DYNAMIC_FPS_THRES[300]
+ else:
+ target_fps = DYNAMIC_FPS_THRES[2400]
+ extract_t = int(effective_duration * target_fps * self.temporal_patch_size)
+ extract_t = min(extract_t, MAX_FRAME_COUNT_DYNAMIC)
+
+ duration_per_frame = 1 / metadata.fps
+ timestamps = [i * duration_per_frame for i in range(total_frames)]
+ max_second = int(duration)
+
+ if total_frames < extract_t:
+ frame_indices = np.linspace(0, total_frames - 1, extract_t, dtype=int).tolist()
+ else:
+ frame_indices = []
+ current_second = 0
+ inv_fps = 1 / (self.temporal_patch_size * target_fps)
+ for frame_index in range(total_frames):
+ if timestamps[frame_index] >= current_second:
+ current_second += inv_fps
+ frame_indices.append(frame_index)
+ if current_second >= max_second:
+ break
+
+ if len(frame_indices) < extract_t:
+ if len(frame_indices) == 0:
+ start, end = 0, max(total_frames - 1, 0)
+ else:
+ start, end = frame_indices[0], frame_indices[-1]
+ frame_indices = np.linspace(start, end, extract_t, dtype=int).tolist()
+ elif len(frame_indices) > extract_t:
+ frame_indices = np.linspace(0, total_frames - 1, extract_t, dtype=int).tolist()
+
+ seen, uniq = set(), []
+ for idx in frame_indices:
+ if idx not in seen:
+ seen.add(idx)
+ uniq.append(idx)
+
+ if len(uniq) & 1:
+ uniq.append(uniq[-1])
+
+ return np.array(uniq)
+
+
+__all__ = [
+ "Glm46VConfig",
+ "Glm46VModel",
+ "Glm46VPreTrainedModel",
+ "Glm46VForConditionalGeneration",
+ "Glm46VProcessor",
+ "Glm46VImageProcessor",
+ "Glm46VImageProcessorPil",
+ "Glm46VVideoProcessor",
+]
diff --git a/third_party/transformers/src/transformers/models/glm46v/processing_glm46v.py b/third_party/transformers/src/transformers/models/glm46v/processing_glm46v.py
new file mode 100644
index 0000000000000000000000000000000000000000..9dcf7c4856e644417eceae69dc5654564ee36162
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/glm46v/processing_glm46v.py
@@ -0,0 +1,271 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/glm46v/modular_glm46v.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_glm46v.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2025 the HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import numpy as np
+
+from ...image_processing_utils import BatchFeature
+from ...image_utils import ImageInput
+from ...processing_utils import MultiModalData, ProcessingKwargs, ProcessorMixin, Unpack
+from ...tokenization_utils_base import PreTokenizedInput, TextInput
+from ...utils import auto_docstring, logging
+from ...video_utils import VideoInput
+
+
+logger = logging.get_logger(__name__)
+
+
+class Glm46VProcessorKwargs(ProcessingKwargs, total=False):
+ _defaults = {
+ "text_kwargs": {
+ "padding": False,
+ "return_token_type_ids": False,
+ "return_mm_token_type_ids": True,
+ },
+ "videos_kwargs": {"return_metadata": True},
+ }
+
+
+@auto_docstring
+class Glm46VProcessor(ProcessorMixin):
+ def __init__(self, image_processor=None, tokenizer=None, video_processor=None, chat_template=None, **kwargs):
+ self.image_token = "<|image|>" if not hasattr(tokenizer, "image_token") else tokenizer.image_token
+ self.video_token = "<|video|>" if not hasattr(tokenizer, "video_token") else tokenizer.video_token
+ self.image_token_id = (
+ tokenizer.image_token_id
+ if getattr(tokenizer, "image_token_id", None)
+ else tokenizer.convert_tokens_to_ids(self.image_token)
+ )
+ self.video_token_id = (
+ tokenizer.video_token_id
+ if getattr(tokenizer, "video_token_id", None)
+ else tokenizer.convert_tokens_to_ids(self.video_token)
+ )
+ super().__init__(image_processor, tokenizer, video_processor, chat_template=chat_template)
+ self.video_start_id = tokenizer.convert_tokens_to_ids("<|begin_of_video|>")
+ self.video_end_id = tokenizer.convert_tokens_to_ids("<|end_of_video|>")
+
+ @auto_docstring
+ def __call__(
+ self,
+ images: ImageInput | None = None,
+ text: TextInput | PreTokenizedInput | list[TextInput] | list[PreTokenizedInput] = None,
+ videos: VideoInput | None = None,
+ **kwargs: Unpack[Glm46VProcessorKwargs],
+ ) -> BatchFeature:
+ r"""
+ Returns:
+ [`BatchFeature`]: A [`BatchFeature`] with the following fields:
+
+ - **input_ids** -- List of token ids to be fed to a model. Returned when `text` is not `None`.
+ - **attention_mask** -- List of indices specifying which tokens should be attended to by the model (when
+ `return_attention_mask=True` or if *"attention_mask"* is in `self.model_input_names` and if `text` is not
+ `None`).
+ - **pixel_values** -- Pixel values to be fed to a model. Returned when `images` is not `None`.
+ - **pixel_values_videos** -- Pixel values of videos to be fed to a model. Returned when `videos` is not `None`.
+ - **image_grid_thw** -- List of image 3D grid in LLM. Returned when `images` is not `None`.
+ - **video_grid_thw** -- List of video 3D grid in LLM. Returned when `videos` is not `None`.
+ """
+ output_kwargs = self._merge_kwargs(
+ Glm46VProcessorKwargs,
+ tokenizer_init_kwargs=self.tokenizer.init_kwargs,
+ **kwargs,
+ )
+ if images is not None:
+ image_inputs = self.image_processor(images=images, **output_kwargs["images_kwargs"])
+ image_grid_thw = image_inputs["image_grid_thw"]
+ else:
+ image_inputs = {}
+ image_grid_thw = None
+
+ if videos is not None:
+ videos_inputs = self.video_processor(videos=videos, **output_kwargs["videos_kwargs"])
+ # If user has not requested video metadata, pop it
+ if not kwargs.get("return_metadata"):
+ video_metadata = videos_inputs.pop("video_metadata")
+ else:
+ video_metadata = videos_inputs["video_metadata"]
+ video_grid_thw = videos_inputs["video_grid_thw"]
+ else:
+ videos_inputs = {}
+ video_grid_thw = None
+
+ if not isinstance(text, list):
+ text = [text]
+
+ text = text.copy() # below lines change text in-place
+ if image_grid_thw is not None:
+ merge_length = self.image_processor.merge_size**2
+ index = 0
+ for i in range(len(text)):
+ while self.image_token in text[i]:
+ num_image_tokens = image_grid_thw[index].prod() // merge_length
+ text[i] = text[i].replace(self.image_token, "<|placeholder|>" * num_image_tokens, 1)
+ index += 1
+ text[i] = text[i].replace("<|placeholder|>", self.image_token)
+
+ if video_grid_thw is not None:
+ merge_length = self.video_processor.merge_size**2
+ video_index = 0
+ for i in range(len(text)):
+ while self.video_token in text[i]:
+ num_frames = video_grid_thw[video_index][0]
+ video_structure = ""
+
+ metadata = video_metadata[video_index]
+ if metadata.fps is None:
+ logger.warning_once(
+ "SmolVLM requires frame timestamps to construct prompts, but the `fps` of the input video could not be inferred. "
+ "Probably `video_metadata` was missing from inputs and you passed pre-sampled frames. "
+ "Defaulting to `fps=24`. Please provide `video_metadata` for more accurate results."
+ )
+ metadata.fps = 24 if metadata.fps is None else metadata.fps
+ timestamps = metadata.timestamps[::2] # mrope
+
+ unique_timestamps = []
+ for idx in range(0, len(timestamps)):
+ unique_timestamps.append(timestamps[idx])
+
+ selected_timestamps = unique_timestamps[:num_frames]
+ while len(selected_timestamps) < num_frames:
+ selected_timestamps.append(selected_timestamps[-1] if selected_timestamps else 0)
+
+ for frame_idx in range(num_frames):
+ timestamp_sec = selected_timestamps[frame_idx]
+ frame_structure = self.replace_frame_token_id(timestamp_sec)
+ video_structure += frame_structure
+
+ text[i] = text[i].replace(self.video_token, video_structure, 1)
+ num_image_tokens = (
+ video_grid_thw[video_index].prod() // merge_length // video_grid_thw[video_index][0]
+ )
+ for frame_idx in range(num_frames):
+ if self.image_token in text[i]:
+ text[i] = text[i].replace(self.image_token, "<|placeholder|>" * num_image_tokens, 1)
+
+ video_index += 1
+
+ text[i] = text[i].replace("<|placeholder|>", self.image_token)
+ return_tensors = output_kwargs["text_kwargs"].pop("return_tensors", None)
+ return_mm_token_type_ids = output_kwargs["text_kwargs"].pop("return_mm_token_type_ids", False)
+ text_inputs = self.tokenizer(text, **output_kwargs["text_kwargs"])
+ self._check_special_mm_tokens(text, text_inputs, modalities=["image", "video"])
+
+ if return_mm_token_type_ids:
+ text_inputs["mm_token_type_ids"] = self.create_mm_token_type_ids(text_inputs["input_ids"])
+ return BatchFeature(data={**text_inputs, **image_inputs, **videos_inputs}, tensor_type=return_tensors)
+
+ def _get_num_multimodal_tokens(self, image_sizes=None, video_sizes=None, **kwargs):
+ """
+ Computes the number of placeholder tokens needed for multimodal inputs with the given sizes.
+ Args:
+ image_sizes (`list[list[int]]`, *optional*):
+ The input sizes formatted as (height, width) per each image.
+ video_sizes (`list[list[int]]`, *optional*):
+ The input sizes formatted as (num_frames, height, width) per each video.
+ Returns:
+ `MultiModalData`: A `MultiModalData` object holding number of tokens per each of the provided
+ input modalities, along with other useful data.
+ """
+
+ vision_data = {}
+ if image_sizes is not None:
+ images_kwargs = Glm46VProcessorKwargs._defaults.get("images_kwargs", {})
+ images_kwargs.update(kwargs)
+ merge_size = images_kwargs.get("merge_size", None) or self.image_processor.merge_size
+
+ num_image_patches = [
+ self.image_processor.get_number_of_image_patches(*image_size, images_kwargs)
+ for image_size in image_sizes
+ ]
+ num_image_tokens = [(num_patches // merge_size**2) for num_patches in num_image_patches]
+ vision_data.update({"num_image_tokens": num_image_tokens, "num_image_patches": num_image_patches})
+
+ if video_sizes is not None:
+ videos_kwargs = Glm46VProcessorKwargs._defaults.get("videos_kwargs", {})
+ videos_kwargs.update(kwargs)
+ num_video_patches = [
+ self.video_processor.get_number_of_video_patches(*video_size, videos_kwargs)
+ for video_size in video_sizes
+ ]
+ num_video_tokens = [(num_patches // merge_size**2) for num_patches in num_video_patches]
+ vision_data["num_video_tokens"] = num_video_tokens
+
+ return MultiModalData(**vision_data)
+
+ def post_process_image_text_to_text(
+ self, generated_outputs, skip_special_tokens=True, clean_up_tokenization_spaces=False, **kwargs
+ ):
+ """
+ Post-process the output of the model to decode the text.
+
+ Args:
+ generated_outputs (`torch.Tensor` or `np.ndarray`):
+ The output of the model `generate` function. The output is expected to be a tensor of shape `(batch_size, sequence_length)`
+ or `(sequence_length,)`.
+ skip_special_tokens (`bool`, *optional*, defaults to `True`):
+ Whether or not to remove special tokens in the output. Argument passed to the tokenizer's `batch_decode` method.
+ clean_up_tokenization_spaces (`bool`, *optional*, defaults to `False`):
+ Whether or not to clean up the tokenization spaces. Argument passed to the tokenizer's `batch_decode` method.
+ **kwargs:
+ Additional arguments to be passed to the tokenizer's `batch_decode method`.
+
+ Returns:
+ `list[str]`: The decoded text.
+ """
+ return self.tokenizer.batch_decode(
+ generated_outputs,
+ skip_special_tokens=skip_special_tokens,
+ clean_up_tokenization_spaces=clean_up_tokenization_spaces,
+ **kwargs,
+ )
+
+ @property
+ def model_input_names(self):
+ model_input_names = super().model_input_names
+ model_input_names.append("mm_token_type_ids")
+ return model_input_names
+
+ def create_mm_token_type_ids(self, input_ids: list) -> list[list[int]]:
+ # We have to iterate for each list separately because inputs
+ # might be non-padded lists and we can't cast numpy on that!
+ # Then cast numpy as each input for faster indexing
+ mm_token_type_ids = []
+ for input in input_ids:
+ array_ids = np.array(input)
+ mm_token_types = np.zeros_like(input)
+
+ # Replace 0 -> 2 only inside video segments because Glm46V
+ # uses the same special token to denote images and video
+ # Otherwise replace 0 -> 1 for image modality
+ starts = np.cumsum(array_ids == self.video_start_id, axis=0)
+ ends = np.cumsum(array_ids == self.video_end_id, axis=0)
+ is_video_modality = starts > ends
+
+ mm_token_types[(array_ids == self.image_token_id) & is_video_modality] = 2
+ mm_token_types[(array_ids == self.image_token_id) & (~is_video_modality)] = 1
+ mm_token_type_ids.append(mm_token_types.tolist())
+ return mm_token_type_ids
+
+ def replace_frame_token_id(self, timestamp_sec):
+ return f"<|begin_of_image|>{self.image_token}<|end_of_image|>{timestamp_sec:.1f} seconds"
+
+
+__all__ = ["Glm46VProcessor"]
diff --git a/third_party/transformers/src/transformers/models/glm46v/video_processing_glm46v.py b/third_party/transformers/src/transformers/models/glm46v/video_processing_glm46v.py
new file mode 100644
index 0000000000000000000000000000000000000000..5ca284d618561f742601b9180b0d944751cd2669
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/glm46v/video_processing_glm46v.py
@@ -0,0 +1,273 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/glm46v/modular_glm46v.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_glm46v.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2025 the HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import numpy as np
+import torch
+from torchvision.transforms.v2 import functional as tvF
+
+from ...image_processing_utils import BatchFeature
+from ...image_utils import (
+ OPENAI_CLIP_MEAN,
+ OPENAI_CLIP_STD,
+ ChannelDimension,
+ PILImageResampling,
+ SizeDict,
+ get_image_size,
+)
+from ...processing_utils import Unpack, VideosKwargs
+from ...utils import TensorType, add_start_docstrings
+from ...video_processing_utils import BASE_VIDEO_PROCESSOR_DOCSTRING, BaseVideoProcessor
+from ...video_utils import VideoMetadata, group_videos_by_shape, reorder_videos
+from .image_processing_glm46v import smart_resize
+
+
+class Glm46VVideoProcessorInitKwargs(VideosKwargs, total=False):
+ max_image_size: dict[str, int]
+ patch_size: int
+ temporal_patch_size: int
+ merge_size: int
+ max_duration: int
+
+
+@add_start_docstrings(
+ "Constructs a fast GLM-4V image processor that dynamically resizes videos based on the original videos.",
+ BASE_VIDEO_PROCESSOR_DOCSTRING,
+ """
+ patch_size (`int`, *optional*, defaults to 14):
+ The spacial patch size of the vision encoder.
+ temporal_patch_size (`int`, *optional*, defaults to 2):
+ The temporal patch size of the vision encoder.
+ merge_size (`int`, *optional*, defaults to 2):
+ The merge size of the vision encoder to llm encoder.
+ """,
+)
+class Glm46VVideoProcessor(BaseVideoProcessor):
+ resample = PILImageResampling.BICUBIC
+ size = {"shortest_edge": 112 * 112, "longest_edge": 28 * 28 * 2 * 30000}
+ max_image_size = {"longest_edge": 28 * 28 * 2 * 30000}
+ image_mean = OPENAI_CLIP_MEAN
+ image_std = OPENAI_CLIP_STD
+ do_resize = True
+ do_rescale = True
+ do_normalize = True
+ do_convert_rgb = True
+ do_sample_frames = True
+ patch_size = 14
+ temporal_patch_size = 2
+ max_duration = 300
+ merge_size = 2
+ valid_kwargs = Glm46VVideoProcessorInitKwargs
+ num_frames = 16
+ fps = 2
+
+ model_input_names = ["pixel_values_videos", "video_grid_thw"]
+
+ def __init__(self, **kwargs: Unpack[Glm46VVideoProcessorInitKwargs]):
+ super().__init__(**kwargs)
+
+ def _standardize_kwargs(self, **kwargs) -> dict:
+ """
+ Update kwargs that need further processing before being validated
+ Can be overridden by subclasses to customize the processing of kwargs.
+ """
+ kwargs = super()._standardize_kwargs(**kwargs)
+ size = kwargs.get("size", self.size)
+ if not size.shortest_edge or not size.longest_edge:
+ raise ValueError("size must contain 'shortest_edge' and 'longest_edge' keys.")
+ return kwargs
+
+ def sample_frames(
+ self,
+ metadata: VideoMetadata,
+ fps: int | float | None = None,
+ **kwargs,
+ ):
+ """
+ Args:
+ metadata (`VideoMetadata`):
+ Metadata of the video containing information about total duration, fps and total number of frames.
+ fps (`int` or `float`, *optional*):
+ Target frames to sample per second. Defaults to `self.fps`.
+ Returns:
+ np.ndarray:
+ Indices to sample video frames.
+ """
+ if metadata is None or getattr(metadata, "fps", None) is None:
+ raise ValueError(
+ "Asked to sample frames per second but no video metadata was provided which is required when sampling in Glm46V. "
+ "Please pass in `VideoMetadata` object or set `do_sample_frames=False`"
+ )
+
+ total_frames = metadata.total_num_frames
+ max_frame_idx = total_frames - 1
+ duration = metadata.duration or round(max_frame_idx / metadata.fps) + 1
+
+ DYNAMIC_FPS_THRES = {30: 3, 300: 1, 2400: 0.5}
+ MAX_FRAME_COUNT_DYNAMIC = 640
+ MAX_DURATION = 2400
+ effective_duration = min(duration, MAX_DURATION)
+ if effective_duration <= 30:
+ target_fps = DYNAMIC_FPS_THRES[30]
+ elif effective_duration <= 300:
+ target_fps = DYNAMIC_FPS_THRES[300]
+ else:
+ target_fps = DYNAMIC_FPS_THRES[2400]
+ extract_t = int(effective_duration * target_fps * self.temporal_patch_size)
+ extract_t = min(extract_t, MAX_FRAME_COUNT_DYNAMIC)
+
+ duration_per_frame = 1 / metadata.fps
+ timestamps = [i * duration_per_frame for i in range(total_frames)]
+ max_second = int(duration)
+
+ if total_frames < extract_t:
+ frame_indices = np.linspace(0, total_frames - 1, extract_t, dtype=int).tolist()
+ else:
+ frame_indices = []
+ current_second = 0
+ inv_fps = 1 / (self.temporal_patch_size * target_fps)
+ for frame_index in range(total_frames):
+ if timestamps[frame_index] >= current_second:
+ current_second += inv_fps
+ frame_indices.append(frame_index)
+ if current_second >= max_second:
+ break
+
+ if len(frame_indices) < extract_t:
+ if len(frame_indices) == 0:
+ start, end = 0, max(total_frames - 1, 0)
+ else:
+ start, end = frame_indices[0], frame_indices[-1]
+ frame_indices = np.linspace(start, end, extract_t, dtype=int).tolist()
+ elif len(frame_indices) > extract_t:
+ frame_indices = np.linspace(0, total_frames - 1, extract_t, dtype=int).tolist()
+
+ seen, uniq = set(), []
+ for idx in frame_indices:
+ if idx not in seen:
+ seen.add(idx)
+ uniq.append(idx)
+
+ if len(uniq) & 1:
+ uniq.append(uniq[-1])
+
+ return np.array(uniq)
+
+ def _preprocess(
+ self,
+ videos: list[torch.Tensor],
+ do_convert_rgb: bool = True,
+ do_resize: bool = True,
+ size: SizeDict | None = None,
+ resample: "PILImageResampling | tvF.InterpolationMode | int | None" = PILImageResampling.BICUBIC,
+ do_rescale: bool = True,
+ rescale_factor: float = 1 / 255.0,
+ do_normalize: bool = True,
+ image_mean: float | list[float] | None = None,
+ image_std: float | list[float] | None = None,
+ patch_size: int | None = None,
+ temporal_patch_size: int | None = None,
+ merge_size: int | None = None,
+ return_tensors: str | TensorType | None = None,
+ **kwargs,
+ ):
+ grouped_videos, grouped_videos_index = group_videos_by_shape(videos)
+ resized_videos_grouped = {}
+
+ for shape, stacked_videos in grouped_videos.items():
+ B, T, C, H, W = stacked_videos.shape
+ num_frames, height, width = T, H, W
+ if do_resize:
+ resized_height, resized_width = smart_resize(
+ num_frames=num_frames,
+ height=height,
+ width=width,
+ temporal_factor=temporal_patch_size,
+ factor=patch_size * merge_size,
+ min_pixels=size.shortest_edge,
+ max_pixels=size.longest_edge,
+ )
+ stacked_videos = stacked_videos.view(B * T, C, H, W)
+ stacked_videos = self.resize(
+ stacked_videos,
+ size=SizeDict(height=resized_height, width=resized_width),
+ resample=resample,
+ )
+ stacked_videos = stacked_videos.view(B, T, C, resized_height, resized_width)
+ resized_videos_grouped[shape] = stacked_videos
+ resized_videos = reorder_videos(resized_videos_grouped, grouped_videos_index)
+
+ # Group videos by size for further processing
+ # Needed in case do_resize is False, or resize returns videos with different sizes
+ grouped_videos, grouped_videos_index = group_videos_by_shape(resized_videos)
+ processed_videos_grouped = {}
+ processed_grids = {}
+ for shape, stacked_videos in grouped_videos.items():
+ resized_height, resized_width = get_image_size(stacked_videos[0], channel_dim=ChannelDimension.FIRST)
+
+ # Fused rescale and normalize
+ stacked_videos = self.rescale_and_normalize(
+ stacked_videos, do_rescale, rescale_factor, do_normalize, image_mean, image_std
+ )
+ patches = stacked_videos
+
+ # Check that videos have `num_frames` divisible by `temporal_patch_size`
+ if patches.shape[1] % temporal_patch_size != 0:
+ repeats = patches[:, -1:].repeat(1, temporal_patch_size - 1, 1, 1, 1)
+ patches = torch.cat([patches, repeats], dim=1)
+ batch_size, grid_t, channel = patches.shape[:3]
+ grid_t = grid_t // temporal_patch_size
+ grid_h, grid_w = resized_height // patch_size, resized_width // patch_size
+
+ patches = patches.view(
+ batch_size,
+ grid_t,
+ temporal_patch_size,
+ channel,
+ grid_h // merge_size,
+ merge_size,
+ patch_size,
+ grid_w // merge_size,
+ merge_size,
+ patch_size,
+ )
+ patches = patches.permute(0, 1, 4, 7, 5, 8, 3, 2, 6, 9)
+ flatten_patches = patches.reshape(
+ batch_size,
+ grid_t * grid_h * grid_w,
+ channel * temporal_patch_size * patch_size * patch_size,
+ )
+
+ processed_videos_grouped[shape] = flatten_patches
+ processed_grids[shape] = [[grid_t, grid_h, grid_w]] * batch_size
+
+ processed_videos = reorder_videos(processed_videos_grouped, grouped_videos_index)
+ processed_grids = reorder_videos(processed_grids, grouped_videos_index)
+ pixel_values_videos = torch.cat(processed_videos, dim=0)
+ video_grid_thw = torch.tensor(processed_grids)
+ data = {
+ "pixel_values_videos": pixel_values_videos,
+ "video_grid_thw": video_grid_thw,
+ }
+
+ return BatchFeature(data=data, tensor_type=return_tensors)
+
+
+__all__ = ["Glm46VVideoProcessor"]
diff --git a/third_party/transformers/src/transformers/models/glm4_moe/__init__.py b/third_party/transformers/src/transformers/models/glm4_moe/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..7fcd5dc9cce65ccc83944e483c5b75a3bbe16cd3
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/glm4_moe/__init__.py
@@ -0,0 +1,27 @@
+# Copyright 2025 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_glm4_moe import *
+ from .modeling_glm4_moe import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/glm4_moe/configuration_glm4_moe.py b/third_party/transformers/src/transformers/models/glm4_moe/configuration_glm4_moe.py
new file mode 100644
index 0000000000000000000000000000000000000000..a18123e90b3327d2e370faff2a7e988f2cb7e959
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/glm4_moe/configuration_glm4_moe.py
@@ -0,0 +1,114 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/glm4_moe/modular_glm4_moe.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_glm4_moe.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2025 The ZhipuAI Inc. team and HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...modeling_rope_utils import RopeParameters
+from ...utils import auto_docstring
+
+
+@auto_docstring(checkpoint="zai-org/GLM-4.5")
+@strict
+class Glm4MoeConfig(PreTrainedConfig):
+ r"""
+ n_group (`int`, *optional*, defaults to 1):
+ Number of groups for routed experts.
+ first_k_dense_replace (`int`, *optional*, defaults to 1):
+ Number of dense layers in shallow layers(embed->dense->dense->...->dense->moe->moe...->lm_head).
+ \--k dense layers--/
+
+ Example:
+
+ ```python
+ >>> from transformers import Glm4MoeModel, Glm4MoeConfig
+
+ >>> # Initializing a Glm4Moe style configuration
+ >>> configuration = Glm4MoeConfig()
+
+ >>> # Initializing a model from the GLM-4-MOE-100B-A10B style configuration
+ >>> model = Glm4MoeModel(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "glm4_moe"
+ keys_to_ignore_at_inference = ["past_key_values"]
+
+ # Default tensor parallel plan for base model `Glm4Moe`
+ base_model_tp_plan = {
+ "layers.*.self_attn.q_proj": "colwise",
+ "layers.*.self_attn.k_proj": "colwise",
+ "layers.*.self_attn.v_proj": "colwise",
+ "layers.*.self_attn.o_proj": "rowwise",
+ "layers.*.mlp.experts.gate_up_proj": "packed_colwise",
+ "layers.*.mlp.experts.down_proj": "rowwise",
+ "layers.*.mlp.experts": "moe_tp_experts", # NOTE(3outeille): This needs to be right after down_proj in the dict. Otherwise, the pattern model.layers.*.mlp.experts will have priority over model.layers.*.mlp.experts.down_proj which will assign a wrong TP plan.
+ "layers.*.mlp.shared_experts.gate_proj": "colwise",
+ "layers.*.mlp.shared_experts.up_proj": "colwise",
+ "layers.*.mlp.shared_experts.down_proj": "rowwise",
+ "layers.*.mlp.gate_proj": "colwise",
+ "layers.*.mlp.up_proj": "colwise",
+ "layers.*.mlp.down_proj": "rowwise",
+ }
+ base_model_pp_plan = {
+ "embed_tokens": (["input_ids"], ["inputs_embeds"]),
+ "layers": (["hidden_states", "attention_mask"], ["hidden_states"]),
+ "norm": (["hidden_states"], ["hidden_states"]),
+ }
+ attribute_map = {
+ "num_local_experts": "n_routed_experts",
+ }
+
+ vocab_size: int = 151552
+ hidden_size: int = 4096
+ intermediate_size: int = 10944
+ num_hidden_layers: int = 46
+ num_attention_heads: int = 96
+ num_key_value_heads: int = 8
+ hidden_act: str = "silu"
+ max_position_embeddings: int = 131072
+ initializer_range: float = 0.02
+ rms_norm_eps: float = 1e-5
+ use_cache: bool = True
+ tie_word_embeddings: bool = False
+ rope_parameters: RopeParameters | dict | None = None
+ attention_bias: bool = False
+ attention_dropout: float | int = 0.0
+ moe_intermediate_size: int = 1408
+ num_experts_per_tok: int = 8
+ n_shared_experts: int = 1
+ n_routed_experts: int = 128
+ routed_scaling_factor: float = 1.0
+ n_group: int = 1
+ topk_group: int = 1
+ first_k_dense_replace: int = 1
+ norm_topk_prob: bool = True
+ use_qk_norm: bool = False
+ bos_token_id: int | None = None
+ eos_token_id: int | list[int] | None = None
+ pad_token_id: int | None = None
+
+ def __post_init__(self, **kwargs):
+ kwargs.setdefault("partial_rotary_factor", 0.5) # assign default for BC
+ super().__post_init__(**kwargs)
+
+
+__all__ = ["Glm4MoeConfig"]
diff --git a/third_party/transformers/src/transformers/models/glm4_moe/modeling_glm4_moe.py b/third_party/transformers/src/transformers/models/glm4_moe/modeling_glm4_moe.py
new file mode 100644
index 0000000000000000000000000000000000000000..1bc20c8322d99043765dd40d3e693a523a39731a
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/glm4_moe/modeling_glm4_moe.py
@@ -0,0 +1,651 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/glm4_moe/modular_glm4_moe.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_glm4_moe.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2025 The ZhipuAI Inc. team and HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from collections.abc import Callable
+from typing import Optional
+
+import torch
+import torch.nn.functional as F
+from torch import nn
+
+from ... import initialization as init
+from ...activations import ACT2FN
+from ...cache_utils import Cache, DynamicCache
+from ...generation import GenerationMixin
+from ...integrations import use_experts_implementation, use_kernel_forward_from_hub, use_kernelized_func
+from ...masking_utils import create_causal_mask
+from ...modeling_flash_attention_utils import FlashAttentionKwargs
+from ...modeling_layers import GradientCheckpointingLayer
+from ...modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast
+from ...modeling_rope_utils import ROPE_INIT_FUNCTIONS, dynamic_rope_update
+from ...modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
+from ...processing_utils import Unpack
+from ...utils import TransformersKwargs, auto_docstring, can_return_tuple
+from ...utils.generic import maybe_autocast, merge_with_config_defaults
+from ...utils.output_capturing import capture_outputs
+from .configuration_glm4_moe import Glm4MoeConfig
+
+
+class Glm4MoeRotaryEmbedding(nn.Module):
+ inv_freq: torch.Tensor # fix linting for `register_buffer`
+
+ def __init__(self, config: Glm4MoeConfig, device=None):
+ super().__init__()
+ self.max_seq_len_cached = config.max_position_embeddings
+ self.original_max_seq_len = config.max_position_embeddings
+
+ self.config = config
+
+ self.rope_type = self.config.rope_parameters["rope_type"]
+ rope_init_fn: Callable = self.compute_default_rope_parameters
+ if self.rope_type != "default":
+ rope_init_fn = ROPE_INIT_FUNCTIONS[self.rope_type]
+ inv_freq, self.attention_scaling = rope_init_fn(self.config, device)
+
+ self.register_buffer("inv_freq", inv_freq, persistent=False)
+ self.register_buffer("original_inv_freq", inv_freq.clone(), persistent=False)
+
+ @staticmethod
+ def compute_default_rope_parameters(
+ config: Glm4MoeConfig | None = None,
+ device: Optional["torch.device"] = None,
+ seq_len: int | None = None,
+ ) -> tuple["torch.Tensor", float]:
+ """
+ Computes the inverse frequencies according to the original RoPE implementation
+ Args:
+ config ([`~transformers.PreTrainedConfig`]):
+ The model configuration.
+ device (`torch.device`):
+ The device to use for initialization of the inverse frequencies.
+ seq_len (`int`, *optional*):
+ The current sequence length. Unused for this type of RoPE.
+ Returns:
+ Tuple of (`torch.Tensor`, `float`), containing the inverse frequencies for the RoPE embeddings and the
+ post-processing scaling factor applied to the computed cos/sin (unused in this type of RoPE).
+ """
+ base = config.rope_parameters["rope_theta"]
+ partial_rotary_factor = config.rope_parameters.get("partial_rotary_factor", 1.0)
+ head_dim = getattr(config, "head_dim", None) or config.hidden_size // config.num_attention_heads
+ dim = int(head_dim * partial_rotary_factor)
+
+ attention_factor = 1.0 # Unused in this type of RoPE
+
+ # Compute the inverse frequencies
+ inv_freq = 1.0 / (
+ base ** (torch.arange(0, dim, 2, dtype=torch.int64).to(device=device, dtype=torch.float) / dim)
+ )
+ return inv_freq, attention_factor
+
+ @torch.no_grad()
+ @dynamic_rope_update # power user: used with advanced RoPE types (e.g. dynamic rope)
+ def forward(self, x, position_ids):
+ inv_freq_expanded = self.inv_freq[None, :, None].float().expand(position_ids.shape[0], -1, 1).to(x.device)
+ position_ids_expanded = position_ids[:, None, :].float()
+
+ device_type = x.device.type if isinstance(x.device.type, str) and x.device.type != "mps" else "cpu"
+ with maybe_autocast(device_type=device_type, enabled=False): # Force float32
+ freqs = (inv_freq_expanded.float() @ position_ids_expanded.float()).transpose(1, 2)
+ emb = torch.cat((freqs, freqs), dim=-1)
+ cos = emb.cos() * self.attention_scaling
+ sin = emb.sin() * self.attention_scaling
+
+ return cos.to(dtype=x.dtype), sin.to(dtype=x.dtype)
+
+
+def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
+ """
+ This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
+ num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
+ """
+ batch, num_key_value_heads, slen, head_dim = hidden_states.shape
+ if n_rep == 1:
+ return hidden_states
+ hidden_states = hidden_states[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim)
+ return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
+
+
+def eager_attention_forward(
+ module: nn.Module,
+ query: torch.Tensor,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ attention_mask: torch.Tensor | None,
+ scaling: float,
+ dropout: float = 0.0,
+ **kwargs: Unpack[TransformersKwargs],
+):
+ key_states = repeat_kv(key, module.num_key_value_groups)
+ value_states = repeat_kv(value, module.num_key_value_groups)
+
+ attn_weights = torch.matmul(query, key_states.transpose(2, 3)) * scaling
+ if attention_mask is not None:
+ attn_weights = attn_weights + attention_mask
+
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query.dtype)
+ attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
+ attn_output = torch.matmul(attn_weights, value_states)
+ attn_output = attn_output.transpose(1, 2).contiguous()
+
+ return attn_output, attn_weights
+
+
+def rotate_half(x):
+ """Rotates half the hidden dims of the input."""
+ x1 = x[..., : x.shape[-1] // 2]
+ x2 = x[..., x.shape[-1] // 2 :]
+ return torch.cat((-x2, x1), dim=-1)
+
+
+def apply_rotary_pos_emb(q, k, cos, sin, unsqueeze_dim=1):
+ """Applies Rotary Position Embedding to the query and key tensors.
+
+ Args:
+ q (`torch.Tensor`): The query tensor.
+ k (`torch.Tensor`): The key tensor.
+ cos (`torch.Tensor`): The cosine part of the rotary embedding.
+ sin (`torch.Tensor`): The sine part of the rotary embedding.
+ unsqueeze_dim (`int`, *optional*, defaults to 1):
+ The 'unsqueeze_dim' argument specifies the dimension along which to unsqueeze cos[position_ids] and
+ sin[position_ids] so that they can be properly broadcasted to the dimensions of q and k. For example, note
+ that cos[position_ids] and sin[position_ids] have the shape [batch_size, seq_len, head_dim]. Then, if q and
+ k have the shape [batch_size, heads, seq_len, head_dim], then setting unsqueeze_dim=1 makes
+ cos[position_ids] and sin[position_ids] broadcastable to the shapes of q and k. Similarly, if q and k have
+ the shape [batch_size, seq_len, heads, head_dim], then set unsqueeze_dim=2.
+ Returns:
+ `tuple(torch.Tensor)` comprising of the query and key tensors rotated using the Rotary Position Embedding.
+ """
+ cos = cos.unsqueeze(unsqueeze_dim)
+ sin = sin.unsqueeze(unsqueeze_dim)
+
+ # Keep half or full tensor for later concatenation
+ rotary_dim = cos.shape[-1]
+ q_rot, q_pass = q[..., :rotary_dim], q[..., rotary_dim:]
+ k_rot, k_pass = k[..., :rotary_dim], k[..., rotary_dim:]
+
+ # Apply rotary embeddings on the first half or full tensor
+ q_embed = (q_rot * cos) + (rotate_half(q_rot) * sin)
+ k_embed = (k_rot * cos) + (rotate_half(k_rot) * sin)
+
+ # Concatenate back to full shape
+ q_embed = torch.cat([q_embed, q_pass], dim=-1)
+ k_embed = torch.cat([k_embed, k_pass], dim=-1)
+ return q_embed, k_embed
+
+
+@use_kernelized_func(apply_rotary_pos_emb)
+class Glm4MoeAttention(nn.Module):
+ """Multi-headed attention from 'Attention Is All You Need' paper"""
+
+ def __init__(self, config: Glm4MoeConfig, layer_idx: int | None = None):
+ super().__init__()
+ self.config = config
+ self.layer_idx = layer_idx
+ self.head_dim = getattr(config, "head_dim", config.hidden_size // config.num_attention_heads)
+ self.num_key_value_groups = config.num_attention_heads // config.num_key_value_heads
+ self.scaling = self.head_dim**-0.5
+ self.rope_parameters = config.rope_parameters
+ self.attention_dropout = config.attention_dropout
+ self.is_causal = True
+
+ self.q_proj = nn.Linear(
+ config.hidden_size, config.num_attention_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.k_proj = nn.Linear(
+ config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.v_proj = nn.Linear(
+ config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.o_proj = nn.Linear(config.num_attention_heads * self.head_dim, config.hidden_size, bias=False)
+ self.use_qk_norm = config.use_qk_norm
+ if self.use_qk_norm:
+ self.q_norm = Glm4MoeRMSNorm(self.head_dim, eps=config.rms_norm_eps)
+ self.k_norm = Glm4MoeRMSNorm(self.head_dim, eps=config.rms_norm_eps)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor],
+ attention_mask: torch.Tensor | None,
+ past_key_values: Cache | None = None,
+ **kwargs: Unpack[FlashAttentionKwargs],
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
+ input_shape = hidden_states.shape[:-1]
+ hidden_shape = (*input_shape, -1, self.head_dim)
+
+ query_states = self.q_proj(hidden_states).view(hidden_shape)
+ key_states = self.k_proj(hidden_states).view(hidden_shape)
+ value_states = self.v_proj(hidden_states).view(hidden_shape)
+
+ if self.use_qk_norm: # main diff from Llama
+ query_states = self.q_norm(query_states)
+ key_states = self.k_norm(key_states)
+
+ query_states = query_states.transpose(1, 2)
+ key_states = key_states.transpose(1, 2)
+ value_states = value_states.transpose(1, 2)
+
+ cos, sin = position_embeddings
+ query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin)
+
+ if past_key_values is not None:
+ key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx)
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ query_states,
+ key_states,
+ value_states,
+ attention_mask,
+ dropout=0.0 if not self.training else self.attention_dropout,
+ scaling=self.scaling,
+ **kwargs,
+ )
+
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
+ attn_output = self.o_proj(attn_output)
+ return attn_output, attn_weights
+
+
+class Glm4MoeMLP(nn.Module):
+ def __init__(self, config, intermediate_size=None):
+ super().__init__()
+ self.config = config
+ self.hidden_size = config.hidden_size
+ self.intermediate_size = config.intermediate_size if intermediate_size is None else intermediate_size
+ self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
+ self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
+ self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False)
+ self.act_fn = ACT2FN[config.hidden_act]
+
+ def forward(self, x):
+ down_proj = self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
+ return down_proj
+
+
+class Glm4MoeTopkRouter(nn.Module):
+ def __init__(self, config: Glm4MoeConfig):
+ super().__init__()
+ self.config = config
+ self.top_k = config.num_experts_per_tok
+ self.n_routed_experts = config.n_routed_experts
+ self.routed_scaling_factor = config.routed_scaling_factor
+ self.n_group = config.n_group
+ self.topk_group = config.topk_group
+ self.norm_topk_prob = config.norm_topk_prob
+
+ self.weight = nn.Parameter(torch.empty((self.n_routed_experts, config.hidden_size)))
+ self.register_buffer("e_score_correction_bias", torch.zeros((self.n_routed_experts), dtype=torch.float32))
+
+ def forward(self, hidden_states):
+ hidden_states = hidden_states.view(-1, self.config.hidden_size)
+ router_logits = F.linear(hidden_states.type(torch.float32), self.weight.type(torch.float32))
+ return router_logits
+
+
+@use_kernel_forward_from_hub("RMSNorm")
+class Glm4MoeRMSNorm(nn.Module):
+ def __init__(self, hidden_size, eps: float = 1e-6) -> None:
+ """
+ Glm4MoeRMSNorm is equivalent to T5LayerNorm
+ """
+ super().__init__()
+ self.weight = nn.Parameter(torch.ones(hidden_size))
+ self.variance_epsilon = eps
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ input_dtype = hidden_states.dtype
+ hidden_states = hidden_states.to(torch.float32)
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
+ hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
+ return self.weight * hidden_states.to(input_dtype)
+
+ def extra_repr(self):
+ return f"{tuple(self.weight.shape)}, eps={self.variance_epsilon}"
+
+
+@use_experts_implementation
+class Glm4MoeNaiveMoe(nn.Module):
+ """Collection of expert weights stored as 3D tensors."""
+
+ def __init__(self, config):
+ super().__init__()
+ self.num_experts = config.num_local_experts
+ self.hidden_dim = config.hidden_size
+ self.intermediate_dim = config.moe_intermediate_size
+ self.gate_up_proj = nn.Parameter(torch.empty(self.num_experts, 2 * self.intermediate_dim, self.hidden_dim))
+ self.down_proj = nn.Parameter(torch.empty(self.num_experts, self.hidden_dim, self.intermediate_dim))
+ self.act_fn = ACT2FN[config.hidden_act]
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ top_k_index: torch.Tensor,
+ top_k_weights: torch.Tensor,
+ ) -> torch.Tensor:
+ final_hidden_states = torch.zeros_like(hidden_states)
+ with torch.no_grad():
+ expert_mask = torch.nn.functional.one_hot(top_k_index, num_classes=self.num_experts)
+ expert_mask = expert_mask.permute(2, 1, 0)
+ expert_hit = torch.greater(expert_mask.sum(dim=(-1, -2)), 0).nonzero()
+
+ for expert_idx in expert_hit:
+ expert_idx = expert_idx[0]
+ if expert_idx == self.num_experts:
+ continue
+ top_k_pos, token_idx = torch.where(expert_mask[expert_idx])
+ current_state = hidden_states[token_idx]
+ gate, up = nn.functional.linear(current_state, self.gate_up_proj[expert_idx]).chunk(2, dim=-1)
+ current_hidden_states = self.act_fn(gate) * up
+ current_hidden_states = nn.functional.linear(current_hidden_states, self.down_proj[expert_idx])
+ current_hidden_states = current_hidden_states * top_k_weights[token_idx, top_k_pos, None]
+ final_hidden_states.index_add_(0, token_idx, current_hidden_states.to(final_hidden_states.dtype))
+
+ return final_hidden_states
+
+
+class Glm4MoeMoE(nn.Module):
+ """
+ A mixed expert module containing shared experts.
+ """
+
+ def __init__(self, config):
+ super().__init__()
+ self.config = config
+ self.experts = Glm4MoeNaiveMoe(config)
+ self.gate = Glm4MoeTopkRouter(config)
+ self.shared_experts = Glm4MoeMLP(
+ config=config, intermediate_size=config.moe_intermediate_size * config.n_shared_experts
+ )
+ self.n_routed_experts = config.n_routed_experts
+ self.n_group = config.n_group
+ self.topk_group = config.topk_group
+ self.norm_topk_prob = config.norm_topk_prob
+ self.routed_scaling_factor = config.routed_scaling_factor
+ self.top_k = config.num_experts_per_tok
+
+ def route_tokens_to_experts(self, router_logits):
+ router_logits = router_logits.sigmoid()
+ router_logits_for_choice = router_logits + self.gate.e_score_correction_bias
+ group_scores = (
+ router_logits_for_choice.view(-1, self.n_group, self.n_routed_experts // self.n_group)
+ .topk(2, dim=-1)[0]
+ .sum(dim=-1)
+ )
+ group_idx = torch.topk(group_scores, k=self.topk_group, dim=-1, sorted=False)[1]
+ group_mask = torch.zeros_like(group_scores)
+ group_mask.scatter_(1, group_idx, 1)
+ score_mask = (
+ group_mask.unsqueeze(-1)
+ .expand(-1, self.n_group, self.n_routed_experts // self.n_group)
+ .reshape(-1, self.n_routed_experts)
+ )
+ scores_for_choice = router_logits_for_choice.masked_fill(~score_mask.bool(), 0.0)
+ topk_indices = torch.topk(scores_for_choice, k=self.top_k, dim=-1, sorted=False)[1]
+ topk_weights = router_logits.gather(1, topk_indices)
+ if self.norm_topk_prob:
+ denominator = topk_weights.sum(dim=-1, keepdim=True) + 1e-20
+ topk_weights /= denominator
+ topk_weights = topk_weights * self.routed_scaling_factor
+ return topk_indices, topk_weights
+
+ def forward(self, hidden_states):
+ residuals = hidden_states
+ orig_shape = hidden_states.shape
+ router_logits = self.gate(hidden_states)
+ topk_indices, topk_weights = self.route_tokens_to_experts(router_logits)
+ hidden_states = hidden_states.view(-1, hidden_states.shape[-1])
+ hidden_states = self.experts(hidden_states, topk_indices, topk_weights).view(*orig_shape)
+ hidden_states = hidden_states + self.shared_experts(residuals)
+ return hidden_states
+
+
+class Glm4MoeDecoderLayer(GradientCheckpointingLayer):
+ def __init__(self, config: Glm4MoeConfig, layer_idx: int):
+ super().__init__()
+ self.hidden_size = config.hidden_size
+
+ self.self_attn = Glm4MoeAttention(config=config, layer_idx=layer_idx)
+
+ if layer_idx >= config.first_k_dense_replace:
+ self.mlp = Glm4MoeMoE(config)
+ else:
+ self.mlp = Glm4MoeMLP(config)
+
+ self.input_layernorm = Glm4MoeRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.post_attention_layernorm = Glm4MoeRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ use_cache: bool | None = False,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> torch.Tensor:
+ residual = hidden_states
+ hidden_states = self.input_layernorm(hidden_states)
+ # Self Attention
+ hidden_states, _ = self.self_attn(
+ hidden_states=hidden_states,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ position_embeddings=position_embeddings,
+ **kwargs,
+ )
+ hidden_states = residual + hidden_states
+
+ # Fully Connected
+ residual = hidden_states
+ hidden_states = self.post_attention_layernorm(hidden_states)
+ hidden_states = self.mlp(hidden_states)
+ hidden_states = residual + hidden_states
+ return hidden_states
+
+
+@auto_docstring
+class Glm4MoePreTrainedModel(PreTrainedModel):
+ config: Glm4MoeConfig
+ base_model_prefix = "model"
+ supports_gradient_checkpointing = True
+ _no_split_modules = ["Glm4MoeDecoderLayer"]
+ _skip_keys_device_placement = ["past_key_values"]
+ _supports_flash_attn = True
+ _supports_sdpa = True
+ _supports_flex_attn = True
+
+ _can_compile_fullgraph = True
+ _supports_attention_backend = True
+ _can_record_outputs = {
+ "hidden_states": Glm4MoeDecoderLayer,
+ "attentions": Glm4MoeAttention,
+ }
+ _keep_in_fp32_modules_strict = ["e_score_correction_bias"]
+ _keys_to_ignore_on_load_unexpected = [r"model\.layers\.92.*", r"model\.layers\.46.*"]
+
+ @torch.no_grad()
+ def _init_weights(self, module):
+ super()._init_weights(module)
+ if isinstance(module, Glm4MoeTopkRouter):
+ init.normal_(module.weight, mean=0.0, std=self.config.initializer_range)
+ init.zeros_(module.e_score_correction_bias)
+ elif isinstance(module, Glm4MoeNaiveMoe):
+ init.normal_(module.gate_up_proj, mean=0.0, std=self.config.initializer_range)
+ init.normal_(module.down_proj, mean=0.0, std=self.config.initializer_range)
+
+
+@auto_docstring
+class Glm4MoeModel(Glm4MoePreTrainedModel):
+ def __init__(self, config: Glm4MoeConfig):
+ super().__init__(config)
+ self.padding_idx = config.pad_token_id
+ self.vocab_size = config.vocab_size
+
+ self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)
+ self.layers = nn.ModuleList(
+ [Glm4MoeDecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
+ )
+ self.norm = Glm4MoeRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.rotary_emb = Glm4MoeRotaryEmbedding(config=config)
+ self.gradient_checkpointing = False
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @merge_with_config_defaults
+ @capture_outputs
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ use_cache: bool | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> BaseModelOutputWithPast:
+ 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 None:
+ inputs_embeds: torch.Tensor = self.embed_tokens(input_ids)
+
+ if use_cache and past_key_values is None:
+ past_key_values = DynamicCache(config=self.config)
+
+ if position_ids is None:
+ past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
+ position_ids = torch.arange(inputs_embeds.shape[1], device=inputs_embeds.device) + past_seen_tokens
+ position_ids = position_ids.unsqueeze(0)
+
+ causal_mask = create_causal_mask(
+ config=self.config,
+ inputs_embeds=inputs_embeds,
+ attention_mask=attention_mask,
+ past_key_values=past_key_values,
+ position_ids=position_ids,
+ )
+
+ hidden_states = inputs_embeds
+ position_embeddings = self.rotary_emb(hidden_states, position_ids=position_ids)
+
+ for decoder_layer in self.layers[: self.config.num_hidden_layers]:
+ hidden_states = decoder_layer(
+ hidden_states,
+ attention_mask=causal_mask,
+ position_embeddings=position_embeddings,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ hidden_states = self.norm(hidden_states)
+ return BaseModelOutputWithPast(
+ last_hidden_state=hidden_states,
+ past_key_values=past_key_values,
+ )
+
+
+@auto_docstring
+class Glm4MoeForCausalLM(Glm4MoePreTrainedModel, GenerationMixin):
+ _tied_weights_keys = {"lm_head.weight": "model.embed_tokens.weight"}
+ _tp_plan = {"lm_head": "colwise_gather_output"}
+ _pp_plan = {"lm_head": (["hidden_states"], ["logits"])}
+
+ def __init__(self, config):
+ super().__init__(config)
+ self.model = Glm4MoeModel(config)
+ self.vocab_size = config.vocab_size
+ self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> CausalLMOutputWithPast:
+ r"""
+ Example:
+
+ ```python
+ >>> from transformers import AutoTokenizer, Glm4MoeForCausalLM
+
+ >>> model = Glm4MoeForCausalLM.from_pretrained("meta-glm4_moe/Glm4Moe-2-7b-hf")
+ >>> tokenizer = AutoTokenizer.from_pretrained("meta-glm4_moe/Glm4Moe-2-7b-hf")
+
+ >>> prompt = "Hey, are you conscious? Can you talk to me?"
+ >>> inputs = tokenizer(prompt, return_tensors="pt")
+
+ >>> # Generate
+ >>> generate_ids = model.generate(inputs.input_ids, max_length=30)
+ >>> tokenizer.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]
+ "Hey, are you conscious? Can you talk to me?\nI'm not conscious, but I can talk to you."
+ ```"""
+ outputs: BaseModelOutputWithPast = self.model(
+ input_ids=input_ids,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ hidden_states = outputs.last_hidden_state
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(logits=logits, labels=labels, vocab_size=self.config.vocab_size, **kwargs)
+
+ return CausalLMOutputWithPast(
+ loss=loss,
+ logits=logits,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ )
+
+
+__all__ = ["Glm4MoePreTrainedModel", "Glm4MoeModel", "Glm4MoeForCausalLM"]
diff --git a/third_party/transformers/src/transformers/models/glm4_moe/modular_glm4_moe.py b/third_party/transformers/src/transformers/models/glm4_moe/modular_glm4_moe.py
new file mode 100644
index 0000000000000000000000000000000000000000..868018d744b5ead11f66adf9b44890f68135211e
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/glm4_moe/modular_glm4_moe.py
@@ -0,0 +1,203 @@
+# Copyright 2025 The ZhipuAI Inc. team and HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""PyTorch GLM-4-MOE model."""
+
+import torch
+from huggingface_hub.dataclasses import strict
+from torch import nn
+
+from ...configuration_utils import PreTrainedConfig
+from ...modeling_rope_utils import RopeParameters
+from ...utils import auto_docstring, logging
+from ..cohere.modeling_cohere import CohereAttention
+from ..deepseek_v3.modeling_deepseek_v3 import (
+ DeepseekV3DecoderLayer,
+ DeepseekV3ForCausalLM,
+ DeepseekV3MLP,
+ DeepseekV3Model,
+ DeepseekV3PreTrainedModel,
+ DeepseekV3RMSNorm,
+ DeepseekV3TopkRouter,
+)
+from ..glm.modeling_glm import GlmRotaryEmbedding
+from ..gpt_neox.modeling_gpt_neox import apply_rotary_pos_emb # noqa
+
+
+logger = logging.get_logger(__name__)
+
+
+@auto_docstring(checkpoint="zai-org/GLM-4.5")
+@strict
+class Glm4MoeConfig(PreTrainedConfig):
+ r"""
+ n_group (`int`, *optional*, defaults to 1):
+ Number of groups for routed experts.
+ first_k_dense_replace (`int`, *optional*, defaults to 1):
+ Number of dense layers in shallow layers(embed->dense->dense->...->dense->moe->moe...->lm_head).
+ \--k dense layers--/
+
+ Example:
+
+ ```python
+ >>> from transformers import Glm4MoeModel, Glm4MoeConfig
+
+ >>> # Initializing a Glm4Moe style configuration
+ >>> configuration = Glm4MoeConfig()
+
+ >>> # Initializing a model from the GLM-4-MOE-100B-A10B style configuration
+ >>> model = Glm4MoeModel(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "glm4_moe"
+ keys_to_ignore_at_inference = ["past_key_values"]
+
+ # Default tensor parallel plan for base model `Glm4Moe`
+ base_model_tp_plan = {
+ "layers.*.self_attn.q_proj": "colwise",
+ "layers.*.self_attn.k_proj": "colwise",
+ "layers.*.self_attn.v_proj": "colwise",
+ "layers.*.self_attn.o_proj": "rowwise",
+ "layers.*.mlp.experts.gate_up_proj": "packed_colwise",
+ "layers.*.mlp.experts.down_proj": "rowwise",
+ "layers.*.mlp.experts": "moe_tp_experts", # NOTE(3outeille): This needs to be right after down_proj in the dict. Otherwise, the pattern model.layers.*.mlp.experts will have priority over model.layers.*.mlp.experts.down_proj which will assign a wrong TP plan.
+ "layers.*.mlp.shared_experts.gate_proj": "colwise",
+ "layers.*.mlp.shared_experts.up_proj": "colwise",
+ "layers.*.mlp.shared_experts.down_proj": "rowwise",
+ "layers.*.mlp.gate_proj": "colwise",
+ "layers.*.mlp.up_proj": "colwise",
+ "layers.*.mlp.down_proj": "rowwise",
+ }
+ base_model_pp_plan = {
+ "embed_tokens": (["input_ids"], ["inputs_embeds"]),
+ "layers": (["hidden_states", "attention_mask"], ["hidden_states"]),
+ "norm": (["hidden_states"], ["hidden_states"]),
+ }
+ attribute_map = {
+ "num_local_experts": "n_routed_experts",
+ }
+
+ vocab_size: int = 151552
+ hidden_size: int = 4096
+ intermediate_size: int = 10944
+ num_hidden_layers: int = 46
+ num_attention_heads: int = 96
+ num_key_value_heads: int = 8
+ hidden_act: str = "silu"
+ max_position_embeddings: int = 131072
+ initializer_range: float = 0.02
+ rms_norm_eps: float = 1e-5
+ use_cache: bool = True
+ tie_word_embeddings: bool = False
+ rope_parameters: RopeParameters | dict | None = None
+ attention_bias: bool = False
+ attention_dropout: float | int = 0.0
+ moe_intermediate_size: int = 1408
+ num_experts_per_tok: int = 8
+ n_shared_experts: int = 1
+ n_routed_experts: int = 128
+ routed_scaling_factor: float = 1.0
+ n_group: int = 1
+ topk_group: int = 1
+ first_k_dense_replace: int = 1
+ norm_topk_prob: bool = True
+ use_qk_norm: bool = False
+ bos_token_id: int | None = None
+ eos_token_id: int | list[int] | None = None
+ pad_token_id: int | None = None
+
+ def __post_init__(self, **kwargs):
+ kwargs.setdefault("partial_rotary_factor", 0.5) # assign default for BC
+ super().__post_init__(**kwargs)
+
+
+class Glm4MoeRotaryEmbedding(GlmRotaryEmbedding):
+ pass
+
+
+class Glm4MoeAttention(CohereAttention):
+ def __init__(self, config: Glm4MoeConfig, layer_idx: int | None = None):
+ nn.Module.__init__(self)
+ self.config = config
+ self.layer_idx = layer_idx
+ self.head_dim = getattr(config, "head_dim", config.hidden_size // config.num_attention_heads)
+ self.num_key_value_groups = config.num_attention_heads // config.num_key_value_heads
+ self.scaling = self.head_dim**-0.5
+ self.rope_parameters = config.rope_parameters
+ self.attention_dropout = config.attention_dropout
+ self.is_causal = True
+
+ self.q_proj = nn.Linear(
+ config.hidden_size, config.num_attention_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.k_proj = nn.Linear(
+ config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.v_proj = nn.Linear(
+ config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.o_proj = nn.Linear(config.num_attention_heads * self.head_dim, config.hidden_size, bias=False)
+ self.use_qk_norm = config.use_qk_norm
+ if self.use_qk_norm:
+ self.q_norm = Glm4MoeRMSNorm(self.head_dim, eps=config.rms_norm_eps)
+ self.k_norm = Glm4MoeRMSNorm(self.head_dim, eps=config.rms_norm_eps)
+
+
+class Glm4MoeMLP(DeepseekV3MLP):
+ pass
+
+
+class Glm4MoeTopkRouter(DeepseekV3TopkRouter):
+ def __init__(self, config: Glm4MoeConfig):
+ nn.Module.__init__(self)
+ self.config = config
+ self.top_k = config.num_experts_per_tok
+ self.n_routed_experts = config.n_routed_experts
+ self.routed_scaling_factor = config.routed_scaling_factor
+ self.n_group = config.n_group
+ self.topk_group = config.topk_group
+ self.norm_topk_prob = config.norm_topk_prob
+
+ self.weight = nn.Parameter(torch.empty((self.n_routed_experts, config.hidden_size)))
+ self.register_buffer("e_score_correction_bias", torch.zeros((self.n_routed_experts), dtype=torch.float32))
+
+
+class Glm4MoeRMSNorm(DeepseekV3RMSNorm):
+ pass
+
+
+class Glm4MoeDecoderLayer(DeepseekV3DecoderLayer):
+ pass
+
+
+class Glm4MoePreTrainedModel(DeepseekV3PreTrainedModel):
+ _keys_to_ignore_on_load_unexpected = [r"model\.layers\.92.*", r"model\.layers\.46.*"]
+
+
+class Glm4MoeModel(DeepseekV3Model):
+ pass
+
+
+class Glm4MoeForCausalLM(DeepseekV3ForCausalLM):
+ pass
+
+
+__all__ = [
+ "Glm4MoeConfig",
+ "Glm4MoePreTrainedModel",
+ "Glm4MoeModel",
+ "Glm4MoeForCausalLM",
+]
diff --git a/third_party/transformers/src/transformers/models/glm4v_moe/__init__.py b/third_party/transformers/src/transformers/models/glm4v_moe/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..f99578a4be721ecdc5bcbd157fe75f8f16384086
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/glm4v_moe/__init__.py
@@ -0,0 +1,27 @@
+# Copyright 2025 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_glm4v_moe import *
+ from .modeling_glm4v_moe import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/glm4v_moe/configuration_glm4v_moe.py b/third_party/transformers/src/transformers/models/glm4v_moe/configuration_glm4v_moe.py
new file mode 100644
index 0000000000000000000000000000000000000000..0e4d6a9cb19176357337741e0c0754e554fb0b93
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/glm4v_moe/configuration_glm4v_moe.py
@@ -0,0 +1,206 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/glm4v_moe/modular_glm4v_moe.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_glm4v_moe.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2025 The ZhipuAI Inc. team and HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...modeling_rope_utils import RopeParameters
+from ...utils import auto_docstring
+
+
+@auto_docstring(checkpoint="zai-org/GLM-4.5V")
+@strict
+class Glm4vMoeTextConfig(PreTrainedConfig):
+ r"""
+ n_group (`int`, *optional*, defaults to 1):
+ Number of groups for routed experts.
+ first_k_dense_replace (`int`, *optional*, defaults to 1):
+ Number of dense layers in shallow layers(embed->dense->dense->...->dense->moe->moe...->lm_head).
+ \--k dense layers--/
+
+ Example:
+
+ ```python
+ >>> from transformers import Glm4vMoeTextModel, Glm4vMoeConfig
+
+ >>> # Initializing a GLM-4.5V style configuration
+ >>> configuration = Glm4vMoeConfig()
+
+ >>> # Initializing a model from the GLM-4.5V style configuration
+ >>> model = Glm4vMoeTextModel(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "glm4v_moe_text"
+ keys_to_ignore_at_inference = ["past_key_values"]
+ # Default tensor parallel plan for base model `Glm4vMoe`
+ base_model_tp_plan = {
+ "layers.*.self_attn.q_proj": "colwise",
+ "layers.*.self_attn.k_proj": "colwise",
+ "layers.*.self_attn.v_proj": "colwise",
+ "layers.*.self_attn.o_proj": "rowwise",
+ "layers.*.mlp.gate_proj": "colwise",
+ "layers.*.mlp.up_proj": "colwise",
+ "layers.*.mlp.down_proj": "rowwise",
+ }
+ base_model_pp_plan = {
+ "embed_tokens": (["input_ids"], ["inputs_embeds"]),
+ "layers": (["hidden_states", "attention_mask"], ["hidden_states"]),
+ "norm": (["hidden_states"], ["hidden_states"]),
+ }
+ attribute_map = {
+ "num_local_experts": "n_routed_experts",
+ }
+
+ vocab_size: int = 151424
+ hidden_size: int = 4096
+ intermediate_size: int = 10944
+ num_hidden_layers: int = 46
+ num_attention_heads: int = 96
+ num_key_value_heads: int = 8
+ hidden_act: str = "silu"
+ max_position_embeddings: int = 65536
+ initializer_range: float = 0.02
+ rms_norm_eps: float = 1e-5
+ use_cache: bool = True
+ tie_word_embeddings: bool = False
+ rope_parameters: RopeParameters | dict | None = None
+ attention_bias: bool = True
+ attention_dropout: float | int = 0.0
+ moe_intermediate_size: int = 1408
+ num_experts_per_tok: int = 8
+ n_shared_experts: int = 1
+ n_routed_experts: int = 128
+ routed_scaling_factor: float = 1.0
+ n_group: int = 1
+ topk_group: int = 1
+ first_k_dense_replace: int = 1
+ norm_topk_prob: bool = True
+ bos_token_id: int | None = None
+ eos_token_id: int | list[int] | None = None
+ pad_token_id: int | None = None
+ base_config_key = "text_config"
+ ignore_keys_at_rope_validation = {"mrope_section"}
+ router_aux_loss_coef: float = 0.0001
+
+ def __post_init__(self, **kwargs):
+ kwargs.setdefault("partial_rotary_factor", 0.5) # assign default for BC
+ super().__post_init__(**kwargs)
+
+
+@auto_docstring(checkpoint="zai-org/GLM-4.1V-9B-Thinking")
+@strict
+class Glm4vMoeVisionConfig(PreTrainedConfig):
+ r"""
+ out_hidden_size (`int`, *optional*, defaults to 4096):
+ The output hidden size of the vision model.
+
+ Example:
+
+ ```python
+ >>> from transformers import Glm4vMoeVisionConfig, Glm4vMoeVisionModel
+
+ >>> # Initializing a Glm4vMoeVisionConfig GLM-4.1V-9B style configuration
+ >>> configuration = Glm4vMoeVisionConfig()
+
+ >>> # Initializing a model (with random weights) from the GLM-4.1V-9B configuration
+ >>> model = Glm4vMoeVisionModel(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "glm4v_moe_vision"
+ base_config_key = "vision_config"
+
+ depth: int = 24
+ hidden_size: int = 1536
+ hidden_act: str = "silu"
+ attention_bias: bool = False
+ attention_dropout: float | int = 0.0
+ num_heads: int = 12
+ in_channels: int = 3
+ image_size: int | list[int] | tuple[int, int] = 336
+ patch_size: int | list[int] | tuple[int, int] = 14
+ rms_norm_eps: float = 1e-05
+ spatial_merge_size: int = 2
+ temporal_patch_size: int | list[int] | tuple[int, int] = 2
+ out_hidden_size: int = 4096
+ intermediate_size: int = 13696
+ initializer_range: float = 0.02
+
+
+@auto_docstring(checkpoint="zai-org/GLM-4.5V")
+@strict
+class Glm4vMoeConfig(PreTrainedConfig):
+ r"""
+ image_start_token_id (`int`, *optional*, defaults to 151339):
+ The image start token index to encode the start of image.
+ image_end_token_id (`int`, *optional*, defaults to 151340):
+ The image end token index to encode the end of image.
+ video_start_token_id (`int`, *optional*, defaults to 151341):
+ The video start token index to encode the start of video.
+ video_end_token_id (`int`, *optional*, defaults to 151342):
+ The video end token index to encode the end of video.
+
+ ```python
+ >>> from transformers import Glm4vMoeForConditionalGeneration, Glm4vMoeConfig
+
+ >>> # Initializing a GLM-4.5V style configuration
+ >>> configuration = Glm4vMoeConfig()
+
+ >>> # Initializing a model from the GLM-4.5V style configuration
+ >>> model = Glm4vMoeForConditionalGeneration(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "glm4v_moe"
+ sub_configs = {"vision_config": Glm4vMoeVisionConfig, "text_config": Glm4vMoeTextConfig}
+ keys_to_ignore_at_inference = ["past_key_values"]
+
+ text_config: dict | PreTrainedConfig | None = None
+ vision_config: dict | PreTrainedConfig | None = None
+
+ image_token_id: int = 151363
+ video_token_id: int = 151364
+ image_start_token_id: int = 151339
+ image_end_token_id: int = 151340
+ video_start_token_id: int = 151341
+ video_end_token_id: int = 151342
+ tie_word_embeddings: bool = False
+
+ def __post_init__(self, **kwargs):
+ if isinstance(self.vision_config, dict):
+ self.vision_config = self.sub_configs["vision_config"](**self.vision_config)
+ elif self.vision_config is None:
+ self.vision_config = self.sub_configs["vision_config"](**kwargs)
+
+ if isinstance(self.text_config, dict):
+ self.text_config = self.sub_configs["text_config"](**self.text_config)
+ elif self.text_config is None:
+ self.text_config = self.sub_configs["text_config"](**kwargs)
+
+ super().__post_init__(**kwargs)
+
+
+__all__ = ["Glm4vMoeConfig", "Glm4vMoeVisionConfig", "Glm4vMoeTextConfig"]
diff --git a/third_party/transformers/src/transformers/models/glm4v_moe/convert_glm4v_moe_mgt_weights_to_hf.py b/third_party/transformers/src/transformers/models/glm4v_moe/convert_glm4v_moe_mgt_weights_to_hf.py
new file mode 100644
index 0000000000000000000000000000000000000000..9d2248b459b83349fed9a4f40243ed96683d4ef1
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/glm4v_moe/convert_glm4v_moe_mgt_weights_to_hf.py
@@ -0,0 +1,762 @@
+# Copyright 2025 The HuggingFace Inc. team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import json
+import os
+import pickle
+import re
+from pathlib import Path
+
+import torch
+from safetensors.torch import save_file
+
+
+# Avoid Using Megatron Lib
+class UnpicklerWrapper(pickle.Unpickler):
+ def find_class(self, mod_name, name):
+ class DummyClass:
+ def __init__(self, *args, **kwargs):
+ pass
+
+ if mod_name.startswith("megatron") or mod_name.startswith("glm") or mod_name.startswith("__main__"):
+ return DummyClass
+ return super().find_class(mod_name, name)
+
+
+pickle.Unpickler = UnpicklerWrapper
+
+
+def dict_access_multi(a_dict, keys):
+ if len(keys) == 0:
+ return a_dict
+ return dict_access_multi(a_dict[keys[0]], keys[1:])
+
+
+def merge_qkv(
+ sd_list,
+ original_tp,
+ num_attention_heads,
+ multi_query_group_num,
+ attention_dim,
+ interleaved_qkv,
+):
+ group_size = (num_attention_heads // multi_query_group_num + 2) * attention_dim
+ q, k, v = [], [], []
+ for sd in sd_list:
+ if interleaved_qkv:
+ shape = sd.shape
+ q_, k_, v_ = sd.view((multi_query_group_num // original_tp, group_size) + (shape[1:])).split(
+ [
+ (num_attention_heads // multi_query_group_num * attention_dim),
+ attention_dim,
+ attention_dim,
+ ],
+ dim=1,
+ )
+ q_ = q_.reshape((-1,) + (shape[1:]))
+ k_ = k_.reshape((-1,) + (shape[1:]))
+ v_ = v_.reshape((-1,) + (shape[1:]))
+ else:
+ q_, k_, v_ = sd.split(
+ [
+ num_attention_heads * attention_dim // original_tp,
+ multi_query_group_num * attention_dim // original_tp,
+ multi_query_group_num * attention_dim // original_tp,
+ ],
+ dim=0,
+ )
+
+ q.append(q_.clone())
+ k.append(k_.clone())
+ v.append(v_.clone())
+ q = torch.cat(q, dim=0)
+ k = torch.cat(k, dim=0)
+ v = torch.cat(v, dim=0)
+
+ return q, k, v
+
+
+def merge_glu(sd_list):
+ return torch.cat(
+ [sd.chunk(dim=0, chunks=2)[0].clone() for sd in sd_list]
+ + [sd.chunk(dim=0, chunks=2)[1].clone() for sd in sd_list],
+ dim=0,
+ )
+
+
+def merge_glu_vit(sd_list, original_tp=None):
+ if not isinstance(sd_list, list):
+ sd_list = [sd_list]
+ gate_proj = torch.cat([sd.chunk(dim=0, chunks=2)[0].clone() for sd in sd_list], dim=0)
+ up_proj = torch.cat([sd.chunk(dim=0, chunks=2)[1].clone() for sd in sd_list], dim=0)
+ return gate_proj, up_proj
+
+
+def split_glu(sd, cnt, idx):
+ return torch.cat(
+ (
+ sd.chunk(dim=0, chunks=2)[0].chunk(cnt, dim=0)[idx].clone(),
+ sd.chunk(dim=0, chunks=2)[1].chunk(cnt, dim=0)[idx].clone(),
+ ),
+ dim=0,
+ )
+
+
+def find_expert_weight(input_dict, layer_num, fc1=True):
+ if fc1:
+ pattern = re.compile(rf"^decoder\.layers\.{layer_num}\.mlp\.experts\.linear_fc1\.weight(\d+)$")
+ else:
+ pattern = re.compile(rf"^decoder\.layers\.{layer_num}\.mlp\.experts\.linear_fc2\.weight(\d+)$")
+ matched = []
+ for key in input_dict:
+ match = pattern.match(key)
+ if match:
+ weight_num = int(match.group(1))
+ matched.append((weight_num, key))
+ matched.sort(key=lambda x: x[0])
+
+ weights = [None for _ in range(len(matched) * len(input_dict[matched[0][1]]))]
+ for idx, key in matched:
+ for i, weight in enumerate(input_dict[key]):
+ weights[i * len(matched) + idx] = weight
+
+ return weights
+
+
+def merge_tensors(
+ tp_sd,
+ keys,
+ original_tp,
+ target_tp,
+ current_tp,
+ slice_dim=None,
+ merge_fn=None,
+):
+ cnt = original_tp // target_tp
+ offset = cnt * current_tp
+ sd_list = [dict_access_multi(tp_sd[i + offset], keys) for i in range(cnt)]
+ if slice_dim is not None:
+ return torch.cat(sd_list, dim=slice_dim)
+ assert merge_fn is not None
+ return merge_fn(sd_list)
+
+
+def save_sharded_model(state_dict, output_path, max_shard_size_gb=5, num_layers=46, vision_num_layers=24):
+ os.makedirs(output_path, exist_ok=True)
+
+ layered_dict = {}
+ for layer_idx in range(num_layers):
+ layer_key = f"layer_{layer_idx}"
+ layered_dict[layer_key] = {}
+
+ for key, value in state_dict.items():
+ if f"model.language_model.layers.{layer_idx}." in key:
+ if isinstance(value, list):
+ assert len(value) == 1, f"{key} {value}"
+ value = value[0]
+ layered_dict[layer_key][key] = value
+
+ for layer_idx in range(vision_num_layers):
+ layer_key = f"visual_layer_{layer_idx}"
+ layered_dict[layer_key] = {}
+
+ for key, value in state_dict.items():
+ if f"model.visual.blocks.{layer_idx}." in key:
+ layered_dict[layer_key][key] = value
+
+ layered_dict["others"] = {}
+ for key, value in state_dict.items():
+ if not any(f"model.language_model.layers.{i}." in key for i in range(num_layers)) and not any(
+ f"model.visual.blocks.{i}." in key for i in range(vision_num_layers)
+ ):
+ layered_dict["others"][key] = value
+
+ # Determine layer ordering
+ layer_order = []
+ for i in range(num_layers):
+ layer_order.append(f"layer_{i}")
+ for i in range(vision_num_layers):
+ layer_order.append(f"visual_layer_{i}")
+ layer_order.append("others")
+
+ # Calculate sizes and create shards by layer
+ param_sizes = {}
+ shards = []
+ current_shard = {}
+ current_shard_size = 0
+ max_shard_size_bytes = max_shard_size_gb * 1024 * 1024 * 1024
+
+ for layer_key in layer_order:
+ layer_weights = layered_dict[layer_key]
+ layer_size = sum(param.numel() * param.element_size() for param in layer_weights.values())
+ if current_shard_size + layer_size > max_shard_size_bytes and current_shard:
+ shards.append(current_shard)
+ current_shard = {}
+ current_shard_size = 0
+ for param_name, param in layer_weights.items():
+ current_shard[param_name] = param
+ current_shard_size += param.numel() * param.element_size()
+ param_sizes[param_name] = param.numel() * param.element_size()
+ if current_shard:
+ shards.append(current_shard)
+ index_dict = {"metadata": {"total_size": sum(param_sizes.values())}, "weight_map": {}}
+
+ for i, shard in enumerate(shards):
+ shard_filename = f"model-{i + 1:05d}-of-{len(shards):05d}.safetensors"
+ shard_path = os.path.join(output_path, shard_filename)
+
+ for param_name in shard:
+ index_dict["weight_map"][param_name] = shard_filename
+
+ save_file(shard, shard_path, metadata={"format": "pt"})
+ print(f"Saved shard {i + 1}/{len(shards)}: {shard_filename}")
+ print(f" Shard size: {sum(p.numel() * p.element_size() for p in shard.values()) / (1024**3):.2f} GB")
+ print(f" Keys in shard: {len(shard)}")
+
+ index_path = os.path.join(output_path, "model.safetensors.index.json")
+ with open(index_path, "w") as f:
+ json.dump(index_dict, f, indent=2)
+
+ return len(shards)
+
+
+def merge_tp_weights(model_path, output_path, vllm_config_path=None):
+ origin_tp, origin_ep, origin_pp = -1, -1, -1
+
+ check_ep_or_pp_later = False
+ for item in Path(model_path).iterdir():
+ if item.is_dir():
+ match = re.match(r"mp_rank_(\d{2})(?:_(\d{3}))?(?:_(\d{3}))?", item.name)
+ if match:
+ groups = match.groups()
+ tp = int(groups[0])
+ origin_tp = max(origin_tp, tp + 1)
+ # maybe TP-EP or TP-PP, need check later
+ if groups[1] is not None and groups[2] is None:
+ pp = int(groups[1])
+ origin_pp = max(origin_pp, pp + 1)
+ origin_ep = 1
+ check_ep_or_pp_later = True
+ elif groups[1] is not None and groups[2] is not None:
+ pp = int(groups[1])
+ ep = int(groups[2])
+ origin_pp = max(origin_pp, pp + 1)
+ origin_ep = max(origin_ep, ep + 1)
+ else:
+ origin_ep = 1
+ origin_pp = 1
+
+ tensor_names_by_file = {}
+ mgt_sd = {}
+ for item in Path(model_path).iterdir():
+ if item.is_dir():
+ match = re.match(r"mp_rank_(\d{2})(?:_(\d{3}))?(?:_(\d{3}))?$", item.name)
+ if match:
+ groups = match.groups()
+ tp = int(groups[0])
+ pp = int(groups[1]) if groups[1] is not None else 0
+ ep = int(groups[2]) if groups[2] is not None else 0
+
+ file_path = item / "model_optim_rng.pt"
+ assert file_path.exists(), f"model_optim_rng.pt not found in {item}"
+
+ file_sd = torch.load(file_path, map_location="cpu", weights_only=False)
+
+ for k in list(file_sd.keys()):
+ if "_extra_state" in k or "dummy_parameter" in k:
+ file_sd.pop(k)
+
+ mgt_sd[(tp, pp, ep)] = file_sd
+
+ tensor_names = set()
+ if "model" in file_sd:
+ for key in file_sd["model"].keys():
+ tensor_names.add(key)
+ tensor_names_by_file[(tp, pp, ep)] = tensor_names
+
+ change_pp_to_ep = False
+ if check_ep_or_pp_later:
+ prefix_distribution = {}
+
+ for (tp, pp, ep), prefixes in tensor_names_by_file.items():
+ for prefix in prefixes:
+ if prefix not in prefix_distribution:
+ prefix_distribution[prefix] = set()
+ prefix_distribution[prefix].add((tp, pp, ep))
+
+ for prefix, locations in prefix_distribution.items():
+ if len(locations) > 1:
+ pp_values = {loc[1] for loc in locations}
+ if len(pp_values) > 1:
+ print(f"find '{prefix}' in multi ranks {pp_values} the parallelism should be TP-EP")
+ origin_ep = origin_pp
+ origin_pp = 1
+ change_pp_to_ep = True
+ break
+ else:
+ print(f"find '{prefix}' only in one ep, parallelism should be TP-PP")
+ break
+
+ print(f"Detected tensor parallel degree TP={origin_tp} EP={origin_ep} PP={origin_pp}")
+ if origin_tp <= 1 and origin_ep <= 1 and origin_pp <= 1:
+ print("Model is already at TP=1 EP=1 PP=1, no need to merge")
+ return
+ assert max(origin_tp, origin_ep) * origin_pp == len(tensor_names_by_file), "maybe some problem in origin weight"
+
+ organized_sd = {}
+ for (tp, pp, ep), file_sd in mgt_sd.items():
+ if change_pp_to_ep:
+ pp, ep = ep, pp
+ organized_sd.setdefault(pp, {})
+ organized_sd[pp][(ep, tp)] = file_sd
+ find_vpp = "model0" in file_sd
+
+ # support VPP, if each pp rank has n vpp blocks, we will treat the original model
+ # was parallel as pp n * origin_pp
+ if find_vpp:
+ organized_sd_vpp = {}
+ for i in range(origin_pp):
+ for (ep, tp), file_sd in organized_sd[i].items():
+ model_keys = sorted(
+ [key for key in file_sd.keys() if key.startswith("model") and key[5:].isdigit()],
+ key=lambda x: int(x[5:]),
+ )
+ vp_blocks = len(model_keys)
+ for idx, key in enumerate(model_keys):
+ assert key in file_sd, f"model {key} not found"
+ organized_sd_vpp.setdefault(idx * origin_pp + i, {})
+ organized_sd_vpp[idx * origin_pp + i][(ep, tp)] = {"model": file_sd[key]}
+ origin_pp = origin_pp * vp_blocks
+ organized_sd = organized_sd_vpp
+
+ ignore_list = ["_extra_state", "dummy_parameter"]
+ layer_share_list = [
+ "norm",
+ "conv3d",
+ "downsample",
+ "router",
+ "mlp.linear_fc2.bias",
+ "self_attention.linear_proj.bias",
+ "position_embeddings",
+ ]
+
+ full_weights = {}
+
+ vit_layer_offset = 0
+ llm_layer_offset = 0
+ llm_layer_pattern = re.compile(r"^(decoder\.layers\.)(\d+)(\..*)$")
+ vit_layer_pattern = re.compile(r"^(vision_model\.transformer\.layers\.)(\d+)(\..*)$")
+ for pp in sorted(organized_sd.keys()):
+ pp_dict = organized_sd[pp]
+ next_llm_layer_offset = llm_layer_offset
+ next_vit_layer_offset = vit_layer_offset
+ ep_map = {}
+ tp_map = {}
+ tp_seen = set()
+ for (ep, tp), item in pp_dict.items():
+ if tp not in tp_seen:
+ tp_seen.add(tp)
+ tp_map[tp] = item
+ ep_map[ep] = item
+
+ for tp in sorted(tp_map.keys()):
+ sd = tp_map[tp]
+ for full_name, tensor in sd["model"].items():
+ if any(x in full_name for x in ignore_list):
+ continue
+ llm_name_match = llm_layer_pattern.match(full_name)
+ if llm_name_match:
+ # Use a closure to avoid global variable issues
+ def offset_layer(x, offset=llm_layer_offset):
+ nonlocal next_llm_layer_offset
+ _real_layer = int(x.group(2)) + offset
+ next_llm_layer_offset = max(next_llm_layer_offset, _real_layer + 1)
+ return f"{x.group(1)}{_real_layer}{x.group(3)}"
+
+ full_name = llm_layer_pattern.sub(offset_layer, full_name)
+ vit_name_match = vit_layer_pattern.match(full_name)
+ if vit_name_match:
+ # Use a closure to avoid global variable issues
+ def offset_layer(x, offset=vit_layer_offset):
+ nonlocal next_vit_layer_offset
+ _real_layer = int(x.group(2)) + offset
+ next_vit_layer_offset = max(next_vit_layer_offset, _real_layer + 1)
+ return f"{x.group(1)}{_real_layer}{x.group(3)}"
+
+ full_name = vit_layer_pattern.sub(offset_layer, full_name)
+ if layer_share_list and any(x in full_name for x in layer_share_list):
+ if full_name not in full_weights:
+ full_weights[full_name] = tensor
+ else:
+ assert torch.equal(tensor, full_weights[full_name]), (
+ f"detect diff param in tp named: {full_name}"
+ )
+ elif not re.search(r"\.experts\.", full_name):
+ full_weights.setdefault(full_name, [None for _ in range(origin_tp)])
+ full_weights[full_name][tp] = tensor
+
+ for ep in sorted(ep_map.keys()):
+ sd = ep_map[ep]
+ for full_name, tensor in sd["model"].items():
+ if any(x in full_name for x in ignore_list):
+ continue
+ name_match = llm_layer_pattern.match(full_name)
+ if name_match:
+ # Use a closure to avoid global variable issues
+ def offset_layer(x, offset=llm_layer_offset):
+ nonlocal next_llm_layer_offset
+ _real_layer = int(x.group(2)) + offset
+ next_llm_layer_offset = max(next_llm_layer_offset, _real_layer + 1)
+ return f"{x.group(1)}{_real_layer}{x.group(3)}"
+
+ full_name = llm_layer_pattern.sub(offset_layer, full_name)
+ if re.search(r"\.experts\.", full_name):
+ full_weights.setdefault(full_name, [None for _ in range(origin_ep)])
+ full_weights[full_name][ep] = tensor
+ llm_layer_offset = next_llm_layer_offset
+ vit_layer_offset = next_vit_layer_offset
+
+ for k in sorted(full_weights.keys()):
+ item = full_weights[k]
+ if isinstance(item, list):
+ print(f"{k} {len(item)} {item[0].shape} {item[0].dtype}", flush=True)
+ else:
+ print(f"{k} {item.shape} {item.dtype}", flush=True)
+
+ print(f"Loading vLLM configuration file: {vllm_config_path}")
+ with open(vllm_config_path, "r") as f:
+ model_config = json.load(f)
+ print(model_config)
+ text_config = model_config.get("text_config", {})
+ vision_config = model_config.get("vision_config", {})
+
+ num_layers = text_config.get("num_hidden_layers", 46)
+ llm_num_heads = text_config.get("num_attention_heads", 96)
+ num_kv_heads = text_config.get("num_key_value_heads", 8)
+ llm_attn_query_size = text_config.get("llm_attn_query_size", 12288)
+ head_dim = text_config.get("attention_dim", llm_attn_query_size // llm_num_heads)
+ vision_num_layers = vision_config.get("depth", 24)
+ vit_n_head = vision_config.get("num_heads", 12)
+
+ print(
+ f"Model parameters: num_layers={num_layers}, vision_num_layers={vision_num_layers}, "
+ f"num_heads={llm_num_heads}, multi_query_group_num={num_kv_heads}, llm_attn_query_size={llm_attn_query_size}"
+ )
+
+ print("Merging tensor parallel weights...")
+
+ interleaved_qkv = True
+ num_attention_heads = llm_num_heads
+ multi_query_group_num = num_kv_heads
+ attention_dim = head_dim
+ complete_state_dict = {}
+
+ # LLM
+ layer_i = 0
+ while f"decoder.layers.{layer_i}.self_attention.linear_qkv.layer_norm_weight" in full_weights:
+ if f"decoder.layers.{layer_i}.self_attention.linear_qkv.layer_norm_weight" in full_weights:
+ complete_state_dict[f"model.language_model.layers.{layer_i}.input_layernorm.weight"] = full_weights[
+ f"decoder.layers.{layer_i}.self_attention.linear_qkv.layer_norm_weight"
+ ]
+
+ if f"decoder.layers.{layer_i}.pre_mlp_layernorm.weight" in full_weights:
+ complete_state_dict[f"model.language_model.layers.{layer_i}.post_attention_layernorm.weight"] = (
+ full_weights[f"decoder.layers.{layer_i}.pre_mlp_layernorm.weight"]
+ )
+ elif f"decoder.layers.{layer_i}.mlp.linear_fc1.layer_norm_weight" in full_weights:
+ complete_state_dict[f"model.language_model.layers.{layer_i}.post_attention_layernorm.weight"] = (
+ full_weights[f"decoder.layers.{layer_i}.mlp.linear_fc1.layer_norm_weight"]
+ )
+
+ q, k, v = merge_qkv(
+ sd_list=full_weights[f"decoder.layers.{layer_i}.self_attention.linear_qkv.weight"],
+ original_tp=origin_tp,
+ num_attention_heads=num_attention_heads,
+ multi_query_group_num=multi_query_group_num,
+ attention_dim=attention_dim,
+ interleaved_qkv=interleaved_qkv,
+ )
+
+ complete_state_dict[f"model.language_model.layers.{layer_i}.self_attn.q_proj.weight"] = q.clone()
+ complete_state_dict[f"model.language_model.layers.{layer_i}.self_attn.k_proj.weight"] = k.clone()
+ complete_state_dict[f"model.language_model.layers.{layer_i}.self_attn.v_proj.weight"] = v.clone()
+
+ if f"decoder.layers.{layer_i}.self_attention.linear_qkv.bias" in full_weights:
+ q_bias, k_bias, v_bias = merge_qkv(
+ sd_list=full_weights[f"decoder.layers.{layer_i}.self_attention.linear_qkv.bias"],
+ original_tp=origin_tp,
+ num_attention_heads=num_attention_heads,
+ multi_query_group_num=multi_query_group_num,
+ attention_dim=attention_dim,
+ interleaved_qkv=interleaved_qkv,
+ )
+ complete_state_dict[f"model.language_model.layers.{layer_i}.self_attn.q_proj.bias"] = q_bias.clone()
+ complete_state_dict[f"model.language_model.layers.{layer_i}.self_attn.k_proj.bias"] = k_bias.clone()
+ complete_state_dict[f"model.language_model.layers.{layer_i}.self_attn.v_proj.bias"] = v_bias.clone()
+
+ o_proj = torch.cat(full_weights[f"decoder.layers.{layer_i}.self_attention.linear_proj.weight"], dim=1)
+ complete_state_dict[f"model.language_model.layers.{layer_i}.self_attn.o_proj.weight"] = o_proj.clone()
+
+ if f"decoder.layers.{layer_i}.mlp.shared_experts.linear_fc1.weight" in full_weights:
+ routed_expert_fc1_weights = find_expert_weight(full_weights, layer_i, fc1=True)
+ for idx, weight in enumerate(routed_expert_fc1_weights):
+ gate_proj_weight, up_proj_weight = merge_glu_vit([weight])
+ complete_state_dict[f"model.language_model.layers.{layer_i}.mlp.experts.{idx}.gate_proj.weight"] = (
+ gate_proj_weight.clone()
+ )
+ complete_state_dict[f"model.language_model.layers.{layer_i}.mlp.experts.{idx}.up_proj.weight"] = (
+ up_proj_weight.clone()
+ )
+
+ routed_expert_fc2_weights = find_expert_weight(full_weights, layer_i, fc1=False)
+ for idx, weight in enumerate(routed_expert_fc2_weights):
+ complete_state_dict[f"model.language_model.layers.{layer_i}.mlp.experts.{idx}.down_proj.weight"] = (
+ weight.clone()
+ )
+
+ complete_state_dict[f"model.language_model.layers.{layer_i}.mlp.gate.e_score_correction_bias"] = (
+ full_weights[f"decoder.layers.{layer_i}.mlp.router.expert_bias"]
+ )
+
+ complete_state_dict[f"model.language_model.layers.{layer_i}.mlp.gate.weight"] = full_weights[
+ f"decoder.layers.{layer_i}.mlp.router.weight"
+ ]
+
+ gate_proj_weight, up_proj_weight = merge_glu_vit(
+ full_weights[f"decoder.layers.{layer_i}.mlp.shared_experts.linear_fc1.weight"]
+ )
+
+ complete_state_dict[f"model.language_model.layers.{layer_i}.mlp.shared_experts.gate_proj.weight"] = (
+ gate_proj_weight.clone()
+ )
+
+ complete_state_dict[f"model.language_model.layers.{layer_i}.mlp.shared_experts.up_proj.weight"] = (
+ up_proj_weight.clone()
+ )
+
+ complete_state_dict[f"model.language_model.layers.{layer_i}.mlp.shared_experts.down_proj.weight"] = (
+ full_weights[f"decoder.layers.{layer_i}.mlp.shared_experts.linear_fc2.weight"]
+ )
+
+ else:
+ # MLP - Use gate_up_proj
+ gate_proj_weight, up_proj_weight = merge_glu_vit(
+ full_weights[f"decoder.layers.{layer_i}.mlp.linear_fc1.weight"]
+ )
+ complete_state_dict[f"model.language_model.layers.{layer_i}.mlp.gate_proj.weight"] = (
+ gate_proj_weight.clone()
+ )
+ complete_state_dict[f"model.language_model.layers.{layer_i}.mlp.up_proj.weight"] = up_proj_weight.clone()
+ complete_state_dict[f"model.language_model.layers.{layer_i}.mlp.down_proj.weight"] = torch.cat(
+ full_weights[f"decoder.layers.{layer_i}.mlp.linear_fc2.weight"], dim=1
+ )
+ layer_i += 1
+
+ # Embedd Model, LM Head, and Norm
+ embed_tokens = torch.cat(full_weights["embedding.word_embeddings.weight"], dim=0)
+ complete_state_dict["model.language_model.embed_tokens.weight"] = embed_tokens.clone()
+
+ lm_head = torch.cat(full_weights["output_layer.weight"], dim=0)
+ complete_state_dict["lm_head.weight"] = lm_head.clone()
+ complete_state_dict["model.language_model.norm.weight"] = full_weights["decoder.final_layernorm.weight"].clone()
+
+ # VLM
+ for layer_i in range(vision_num_layers):
+ complete_state_dict[f"model.visual.blocks.{layer_i}.norm1.weight"] = full_weights[
+ f"vision_model.transformer.layers.{layer_i}.self_attention.linear_qkv.layer_norm_weight"
+ ]
+ complete_state_dict[f"model.visual.blocks.{layer_i}.norm2.weight"] = full_weights[
+ f"vision_model.transformer.layers.{layer_i}.mlp.linear_fc1.layer_norm_weight"
+ ]
+
+ q, k, v = merge_qkv(
+ sd_list=full_weights[f"vision_model.transformer.layers.{layer_i}.self_attention.linear_qkv.weight"],
+ original_tp=origin_tp,
+ num_attention_heads=vit_n_head,
+ multi_query_group_num=vit_n_head,
+ attention_dim=attention_dim,
+ interleaved_qkv=interleaved_qkv,
+ )
+ complete_state_dict[f"model.visual.blocks.{layer_i}.attn.qkv.weight"] = torch.cat((q, k, v), dim=0)
+
+ proj_weight = torch.cat(
+ full_weights[f"vision_model.transformer.layers.{layer_i}.self_attention.linear_proj.weight"], dim=1
+ )
+ complete_state_dict[f"model.visual.blocks.{layer_i}.attn.proj.weight"] = proj_weight.clone()
+
+ gate_proj_weight, up_proj_weight = merge_glu_vit(
+ full_weights[f"vision_model.transformer.layers.{layer_i}.mlp.linear_fc1.weight"]
+ )
+
+ complete_state_dict[f"model.visual.blocks.{layer_i}.mlp.gate_proj.weight"] = gate_proj_weight.clone()
+ complete_state_dict[f"model.visual.blocks.{layer_i}.mlp.up_proj.weight"] = up_proj_weight.clone()
+
+ down_proj_weight = torch.cat(
+ full_weights[f"vision_model.transformer.layers.{layer_i}.mlp.linear_fc2.weight"], dim=1
+ )
+ complete_state_dict[f"model.visual.blocks.{layer_i}.mlp.down_proj.weight"] = down_proj_weight.clone()
+
+ complete_state_dict["model.visual.downsample.weight"] = (
+ full_weights["vision_model.downsample.weight"].clone().contiguous()
+ )
+ complete_state_dict["model.visual.downsample.bias"] = (
+ full_weights["vision_model.downsample.bias"].clone().contiguous()
+ )
+
+ # Merger
+ gate_proj, up_proj = merge_glu_vit(full_weights["vision_projection.encoder.linear_fc1.weight"])
+
+ down_proj = torch.cat(full_weights["vision_projection.encoder.linear_fc2.weight"], dim=1)
+ proj = torch.cat(full_weights["vision_projection.linear_fc_extra.weight"], dim=0)
+
+ complete_state_dict["model.visual.merger.gate_proj.weight"] = gate_proj.clone().contiguous()
+ complete_state_dict["model.visual.merger.up_proj.weight"] = up_proj.clone().contiguous()
+ complete_state_dict["model.visual.merger.down_proj.weight"] = down_proj.clone().contiguous()
+ complete_state_dict["model.visual.merger.proj.weight"] = proj.clone().contiguous()
+
+ if "vision_projection.layer_norm.weight" in full_weights:
+ complete_state_dict["model.visual.merger.post_projection_norm.weight"] = full_weights[
+ "vision_projection.layer_norm.weight"
+ ]
+ if "vision_projection.layer_norm.bias" in full_weights:
+ complete_state_dict["model.visual.merger.post_projection_norm.bias"] = full_weights[
+ "vision_projection.layer_norm.bias"
+ ]
+
+ complete_state_dict["model.visual.embeddings.position_embedding.weight"] = (
+ full_weights["vision_model.position_embeddings.weight"].clone().contiguous()
+ )
+ complete_state_dict["model.visual.patch_embed.proj.weight"] = (
+ full_weights["vision_model.conv3d.weight"].clone().contiguous()
+ )
+ complete_state_dict["model.visual.patch_embed.proj.bias"] = (
+ full_weights["vision_model.conv3d.bias"].clone().contiguous()
+ )
+
+ # Check for additional vision model norm layers mentioned in the expected output
+ if "vision_model.post_conv_layernorm.weight" in full_weights:
+ complete_state_dict["model.visual.post_conv_layernorm.weight"] = (
+ full_weights["vision_model.post_conv_layernorm.weight"].clone().contiguous()
+ )
+
+ if "vision_model.post_layernorm.weight" in full_weights:
+ complete_state_dict["model.visual.post_layernorm.weight"] = (
+ full_weights["vision_model.post_layernorm.weight"].clone().contiguous()
+ )
+
+ print(f"Total keys in state dict: {len(complete_state_dict)}")
+
+ print("bias use Float32")
+
+ save_sharded_model(
+ complete_state_dict,
+ output_path=output_path,
+ max_shard_size_gb=5,
+ num_layers=num_layers,
+ vision_num_layers=vision_num_layers,
+ )
+
+ hf_config = {
+ "architectures": ["Glm4vMoeForConditionalGeneration"],
+ "model_type": "glm4v_moe",
+ "image_start_token_id": model_config.get("image_start_token_id", 151339),
+ "image_end_token_id": model_config.get("image_end_token_id", 151340),
+ "video_start_token_id": model_config.get("video_start_token_id", 151341),
+ "video_end_token_id": model_config.get("video_end_token_id", 151342),
+ "transformers_version": "4.57.0.dev0",
+ }
+ txt_config = {
+ "model_type": "glm4v_moe_text",
+ "attention_bias": model_config.get("add_qkv_bias", True),
+ "use_qk_norm": model_config.get("use_qk_norm", False),
+ "attention_dropout": 0.0,
+ "pad_token_id": model_config.get("pad_token_id", 151329),
+ "eos_token_id": model_config.get("eos_token_id", [151329, 151336, 151338]),
+ "image_token_id": model_config.get("image_token_id", 151363),
+ "video_token_id": model_config.get("video_token_id", 151364),
+ "hidden_act": text_config.get("hidden_act", "silu"),
+ "hidden_size": text_config.get("hidden_size", 4096),
+ "initializer_range": 0.02,
+ "intermediate_size": text_config.get("intermediate_size", 10944),
+ "max_position_embeddings": text_config.get("seq_length", 131072),
+ "num_attention_heads": text_config.get("num_attention_heads", 96),
+ "num_hidden_layers": text_config.get("num_layers", 46),
+ "num_key_value_heads": text_config.get("multi_query_group_num", 2),
+ "rms_norm_eps": text_config.get("layernorm_epsilon", 1e-05),
+ "dtype": text_config.get("torch_dtype", "bfloat16"),
+ "use_cache": text_config.get("use_cache", True),
+ "vocab_size": text_config.get("vocab_size", 151424),
+ "partial_rotary_factor": 0.5,
+ "tie_word_embeddings": False,
+ "moe_intermediate_size": text_config.get("moe_intermediate_size", 1408),
+ "n_group": text_config.get("n_group", 1),
+ "n_routed_experts": text_config.get("n_routed_experts", 128),
+ "n_shared_experts": text_config.get("n_shared_experts", 1),
+ "norm_topk_prob": text_config.get("norm_topk_prob", True),
+ "num_experts_per_tok": text_config.get("num_experts_per_tok", 8),
+ "rope_parameters": {
+ "rope_type": "default",
+ "rope_theta": 10000.0,
+ "mrope_section": [8, 12, 12],
+ "partial_rotary_factor": 0.5,
+ },
+ }
+ hf_config["text_config"] = txt_config
+
+ if "vision_config" in model_config:
+ vision_config = {
+ "model_type": "glm4v_moe_vision",
+ "hidden_size": model_config["vision_config"].get("hidden_size", 1536),
+ "depth": model_config["vision_config"].get("num_layers", 24),
+ "num_heads": model_config["vision_config"].get("num_attention_heads", 12),
+ "attention_bias": model_config["vision_config"].get("attention_bias", False),
+ "intermediate_size": model_config.get("ffn_hidden_size", 13696),
+ "hidden_act": model_config["vision_config"].get("hidden_act", "silu"),
+ "hidden_dropout_prob": model_config["vision_config"].get("hidden_dropout_prob", 0.0),
+ "initializer_range": 0.02,
+ "image_size": model_config["vision_config"].get("image_size", 336),
+ "patch_size": model_config["vision_config"].get("patch_size", 14),
+ "out_hidden_size": model_config.get("hidden_size", 4096),
+ "rms_norm_eps": model_config["vision_config"].get("layernorm_epsilon", 1e-05),
+ "spatial_merge_size": model_config["vision_config"].get("downsample_ratio", 2),
+ "temporal_patch_size": model_config["vision_config"].get("t_patch", 2),
+ }
+ hf_config["vision_config"] = vision_config
+
+ config_path = os.path.join(output_path, "config.json")
+ with open(config_path, "w") as f:
+ json.dump(hf_config, f, indent=2)
+
+ print(f"Conversion complete! Model saved to {output_path}")
+
+
+def parse_args():
+ parser = argparse.ArgumentParser(description="Convert Megatron model to HuggingFace format")
+ parser.add_argument(
+ "--model_path",
+ type=str,
+ required=True,
+ help="Path to Megatron model directory",
+ )
+ parser.add_argument("--output_path", type=str, required=True, help="Output path for HuggingFace model directory")
+ parser.add_argument(
+ "--config_path", type=str, help="Path to vLLM configuration file for creating HuggingFace config"
+ )
+ return parser.parse_args()
+
+
+if __name__ == "__main__":
+ args = parse_args()
+ merge_tp_weights(args.model_path, args.output_path, args.config_path)
diff --git a/third_party/transformers/src/transformers/models/glm4v_moe/modeling_glm4v_moe.py b/third_party/transformers/src/transformers/models/glm4v_moe/modeling_glm4v_moe.py
new file mode 100644
index 0000000000000000000000000000000000000000..363e4269f3a6f39a322bf9c1bdfa14e11a5b3db0
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/glm4v_moe/modeling_glm4v_moe.py
@@ -0,0 +1,1972 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/glm4v_moe/modular_glm4v_moe.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_glm4v_moe.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2025 The ZhipuAI Inc. team and HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import itertools
+from collections.abc import Callable
+from dataclasses import dataclass
+from typing import Any, Optional
+
+import torch
+import torch.nn as nn
+import torch.nn.functional as F
+from torch.nn import LayerNorm
+
+from ... import initialization as init
+from ...activations import ACT2FN
+from ...cache_utils import Cache, DynamicCache
+from ...generation import GenerationMixin
+from ...integrations import use_experts_implementation, use_kernel_forward_from_hub, use_kernelized_func
+from ...masking_utils import create_causal_mask
+from ...modeling_flash_attention_utils import FlashAttentionKwargs
+from ...modeling_layers import GradientCheckpointingLayer
+from ...modeling_outputs import BaseModelOutputWithPooling, ModelOutput, MoeModelOutputWithPast
+from ...modeling_rope_utils import ROPE_INIT_FUNCTIONS, dynamic_rope_update
+from ...modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
+from ...processing_utils import Unpack
+from ...utils import TransformersKwargs, auto_docstring, torch_compilable_check
+from ...utils.generic import can_return_tuple, is_flash_attention_requested, maybe_autocast, merge_with_config_defaults
+from ...utils.output_capturing import capture_outputs
+from .configuration_glm4v_moe import Glm4vMoeConfig, Glm4vMoeTextConfig, Glm4vMoeVisionConfig
+
+
+def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
+ """
+ This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
+ num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
+ """
+ batch, num_key_value_heads, slen, head_dim = hidden_states.shape
+ if n_rep == 1:
+ return hidden_states
+ hidden_states = hidden_states[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim)
+ return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
+
+
+def eager_attention_forward(
+ module: nn.Module,
+ query: torch.Tensor,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ attention_mask: torch.Tensor | None,
+ scaling: float,
+ dropout: float = 0.0,
+ **kwargs: Unpack[TransformersKwargs],
+):
+ key_states = repeat_kv(key, module.num_key_value_groups)
+ value_states = repeat_kv(value, module.num_key_value_groups)
+
+ attn_weights = torch.matmul(query, key_states.transpose(2, 3)) * scaling
+ if attention_mask is not None:
+ attn_weights = attn_weights + attention_mask
+
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query.dtype)
+ attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
+ attn_output = torch.matmul(attn_weights, value_states)
+ attn_output = attn_output.transpose(1, 2).contiguous()
+
+ return attn_output, attn_weights
+
+
+def rotate_half(x):
+ """Rotates half the hidden dims of the input."""
+ x1 = x[..., : x.shape[-1] // 2]
+ x2 = x[..., x.shape[-1] // 2 :]
+ return torch.cat((-x2, x1), dim=-1)
+
+
+def apply_rotary_pos_emb(q, k, cos, sin, unsqueeze_dim=1):
+ """Applies Rotary Position Embedding to the query and key tensors.
+
+ Args:
+ q (`torch.Tensor`): The query tensor.
+ k (`torch.Tensor`): The key tensor.
+ cos (`torch.Tensor`): The cosine part of the rotary embedding.
+ sin (`torch.Tensor`): The sine part of the rotary embedding.
+ unsqueeze_dim (`int`, *optional*, defaults to 1):
+ The 'unsqueeze_dim' argument specifies the dimension along which to unsqueeze cos[position_ids] and
+ sin[position_ids] so that they can be properly broadcasted to the dimensions of q and k. For example, note
+ that cos[position_ids] and sin[position_ids] have the shape [batch_size, seq_len, head_dim]. Then, if q and
+ k have the shape [batch_size, heads, seq_len, head_dim], then setting unsqueeze_dim=1 makes
+ cos[position_ids] and sin[position_ids] broadcastable to the shapes of q and k. Similarly, if q and k have
+ the shape [batch_size, seq_len, heads, head_dim], then set unsqueeze_dim=2.
+ Returns:
+ `tuple(torch.Tensor)` comprising of the query and key tensors rotated using the Rotary Position Embedding.
+ """
+ cos = cos.unsqueeze(unsqueeze_dim)
+ sin = sin.unsqueeze(unsqueeze_dim)
+
+ # Keep half or full tensor for later concatenation
+ rotary_dim = cos.shape[-1]
+ q_rot, q_pass = q[..., :rotary_dim], q[..., rotary_dim:]
+ k_rot, k_pass = k[..., :rotary_dim], k[..., rotary_dim:]
+
+ # Apply rotary embeddings on the first half or full tensor
+ q_embed = (q_rot * cos) + (rotate_half(q_rot) * sin)
+ k_embed = (k_rot * cos) + (rotate_half(k_rot) * sin)
+
+ # Concatenate back to full shape
+ q_embed = torch.cat([q_embed, q_pass], dim=-1)
+ k_embed = torch.cat([k_embed, k_pass], dim=-1)
+ return q_embed, k_embed
+
+
+@use_kernelized_func(apply_rotary_pos_emb)
+class Glm4vMoeTextAttention(nn.Module):
+ """Multi-headed attention from 'Attention Is All You Need' paper"""
+
+ def __init__(self, config: Glm4vMoeTextConfig, layer_idx: int | None = None):
+ super().__init__()
+ self.config = config
+ self.layer_idx = layer_idx
+ self.head_dim = getattr(config, "head_dim", config.hidden_size // config.num_attention_heads)
+ self.num_key_value_groups = config.num_attention_heads // config.num_key_value_heads
+ self.scaling = self.head_dim**-0.5
+ self.attention_dropout = config.attention_dropout
+ self.is_causal = True
+
+ self.q_proj = nn.Linear(
+ config.hidden_size, config.num_attention_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.k_proj = nn.Linear(
+ config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.v_proj = nn.Linear(
+ config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.o_proj = nn.Linear(config.num_attention_heads * self.head_dim, config.hidden_size, bias=False)
+ self.rope_parameters = config.rope_parameters
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor],
+ attention_mask: torch.Tensor | None,
+ past_key_values: Cache | None = None,
+ **kwargs: Unpack[FlashAttentionKwargs],
+ ) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
+ input_shape = hidden_states.shape[:-1]
+ hidden_shape = (*input_shape, -1, self.head_dim)
+
+ query_states = self.q_proj(hidden_states).view(hidden_shape)
+ key_states = self.k_proj(hidden_states).view(hidden_shape)
+ value_states = self.v_proj(hidden_states).view(hidden_shape)
+
+ query_states = query_states.transpose(1, 2)
+ key_states = key_states.transpose(1, 2)
+ value_states = value_states.transpose(1, 2)
+
+ cos, sin = position_embeddings
+ query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin)
+
+ if past_key_values is not None:
+ key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx)
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ query_states,
+ key_states,
+ value_states,
+ attention_mask,
+ dropout=0.0 if not self.training else self.attention_dropout,
+ scaling=self.scaling,
+ **kwargs,
+ )
+
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
+ attn_output = self.o_proj(attn_output)
+ return attn_output, attn_weights
+
+
+class Glm4vMoeTextTopkRouter(nn.Module):
+ def __init__(self, config: Glm4vMoeTextConfig):
+ super().__init__()
+ self.config = config
+ self.top_k = config.num_experts_per_tok
+ self.n_routed_experts = config.n_routed_experts
+ self.routed_scaling_factor = config.routed_scaling_factor
+ self.n_group = config.n_group
+ self.topk_group = config.topk_group
+ self.norm_topk_prob = config.norm_topk_prob
+
+ self.weight = nn.Parameter(torch.empty((self.n_routed_experts, config.hidden_size)))
+ self.register_buffer("e_score_correction_bias", torch.zeros((self.n_routed_experts), dtype=torch.float32))
+
+ def forward(self, hidden_states):
+ hidden_states = hidden_states.view(-1, self.config.hidden_size)
+ router_logits = F.linear(hidden_states.type(torch.float32), self.weight.type(torch.float32))
+ return router_logits
+
+
+@use_experts_implementation
+class Glm4vMoeTextNaiveMoe(nn.Module):
+ """Collection of expert weights stored as 3D tensors."""
+
+ def __init__(self, config):
+ super().__init__()
+ self.num_experts = config.num_local_experts
+ self.hidden_dim = config.hidden_size
+ self.intermediate_dim = config.moe_intermediate_size
+ self.gate_up_proj = nn.Parameter(torch.empty(self.num_experts, 2 * self.intermediate_dim, self.hidden_dim))
+ self.down_proj = nn.Parameter(torch.empty(self.num_experts, self.hidden_dim, self.intermediate_dim))
+ self.act_fn = ACT2FN[config.hidden_act]
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ top_k_index: torch.Tensor,
+ top_k_weights: torch.Tensor,
+ ) -> torch.Tensor:
+ final_hidden_states = torch.zeros_like(hidden_states)
+ with torch.no_grad():
+ expert_mask = torch.nn.functional.one_hot(top_k_index, num_classes=self.num_experts)
+ expert_mask = expert_mask.permute(2, 1, 0)
+ expert_hit = torch.greater(expert_mask.sum(dim=(-1, -2)), 0).nonzero()
+
+ for expert_idx in expert_hit:
+ expert_idx = expert_idx[0]
+ if expert_idx == self.num_experts:
+ continue
+ top_k_pos, token_idx = torch.where(expert_mask[expert_idx])
+ current_state = hidden_states[token_idx]
+ gate, up = nn.functional.linear(current_state, self.gate_up_proj[expert_idx]).chunk(2, dim=-1)
+ current_hidden_states = self.act_fn(gate) * up
+ current_hidden_states = nn.functional.linear(current_hidden_states, self.down_proj[expert_idx])
+ current_hidden_states = current_hidden_states * top_k_weights[token_idx, top_k_pos, None]
+ final_hidden_states.index_add_(0, token_idx, current_hidden_states.to(final_hidden_states.dtype))
+
+ return final_hidden_states
+
+
+class Glm4vMoeTextMoE(nn.Module):
+ """
+ A mixed expert module containing shared experts.
+ """
+
+ def __init__(self, config: Glm4vMoeTextConfig):
+ super().__init__()
+ self.config = config
+ self.experts = Glm4vMoeTextNaiveMoe(config)
+ self.gate = Glm4vMoeTextTopkRouter(config)
+ self.shared_experts = Glm4vMoeTextMLP(
+ config=config, intermediate_size=config.moe_intermediate_size * config.n_shared_experts
+ )
+ self.n_routed_experts = config.n_routed_experts
+ self.n_group = config.n_group
+ self.topk_group = config.topk_group
+ self.norm_topk_prob = config.norm_topk_prob
+ self.routed_scaling_factor = config.routed_scaling_factor
+ self.top_k = config.num_experts_per_tok
+
+ def route_tokens_to_experts(self, router_logits):
+ router_logits = router_logits.sigmoid()
+ router_logits_for_choice = router_logits + self.gate.e_score_correction_bias
+ group_scores = (
+ router_logits_for_choice.view(-1, self.n_group, self.n_routed_experts // self.n_group)
+ .topk(2, dim=-1)[0]
+ .sum(dim=-1)
+ )
+ group_idx = torch.topk(group_scores, k=self.topk_group, dim=-1, sorted=False)[1]
+ group_mask = torch.zeros_like(group_scores)
+ group_mask.scatter_(1, group_idx, 1)
+ score_mask = (
+ group_mask.unsqueeze(-1)
+ .expand(-1, self.n_group, self.n_routed_experts // self.n_group)
+ .reshape(-1, self.n_routed_experts)
+ )
+ scores_for_choice = router_logits_for_choice.masked_fill(~score_mask.bool(), 0.0)
+ topk_indices = torch.topk(scores_for_choice, k=self.top_k, dim=-1, sorted=False)[1]
+ topk_weights = router_logits.gather(1, topk_indices)
+ if self.norm_topk_prob:
+ denominator = topk_weights.sum(dim=-1, keepdim=True) + 1e-20
+ topk_weights /= denominator
+ topk_weights = topk_weights * self.routed_scaling_factor
+ return topk_indices, topk_weights
+
+ def forward(self, hidden_states):
+ residuals = hidden_states
+ orig_shape = hidden_states.shape
+ router_logits = self.gate(hidden_states)
+ topk_indices, topk_weights = self.route_tokens_to_experts(router_logits)
+ hidden_states = hidden_states.view(-1, hidden_states.shape[-1])
+ hidden_states = self.experts(hidden_states, topk_indices, topk_weights).view(*orig_shape)
+ hidden_states = hidden_states + self.shared_experts(residuals)
+ return hidden_states
+
+
+class Glm4vMoeTextMLP(nn.Module):
+ def __init__(self, config, intermediate_size=None):
+ super().__init__()
+ self.config = config
+ self.hidden_size = config.hidden_size
+ self.intermediate_size = config.intermediate_size if intermediate_size is None else intermediate_size
+ self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
+ self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
+ self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False)
+ self.act_fn = ACT2FN[config.hidden_act]
+
+ def forward(self, x):
+ down_proj = self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
+ return down_proj
+
+
+@use_kernel_forward_from_hub("RMSNorm")
+class Glm4vMoeTextRMSNorm(nn.Module):
+ def __init__(self, hidden_size, eps: float = 1e-6) -> None:
+ """
+ Glm4vMoeTextRMSNorm is equivalent to T5LayerNorm
+ """
+ super().__init__()
+ self.weight = nn.Parameter(torch.ones(hidden_size))
+ self.variance_epsilon = eps
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ input_dtype = hidden_states.dtype
+ hidden_states = hidden_states.to(torch.float32)
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
+ hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
+ return self.weight * hidden_states.to(input_dtype)
+
+ def extra_repr(self):
+ return f"{tuple(self.weight.shape)}, eps={self.variance_epsilon}"
+
+
+class Glm4vMoeTextDecoderLayer(GradientCheckpointingLayer):
+ def __init__(self, config: Glm4vMoeTextConfig, layer_idx: int):
+ super().__init__()
+ self.hidden_size = config.hidden_size
+
+ self.self_attn = Glm4vMoeTextAttention(config=config, layer_idx=layer_idx)
+
+ if layer_idx >= config.first_k_dense_replace:
+ self.mlp = Glm4vMoeTextMoE(config)
+ else:
+ self.mlp = Glm4vMoeTextMLP(config)
+
+ self.input_layernorm = Glm4vMoeTextRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.post_attention_layernorm = Glm4vMoeTextRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ use_cache: bool | None = False,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> torch.Tensor:
+ residual = hidden_states
+ hidden_states = self.input_layernorm(hidden_states)
+ # Self Attention
+ hidden_states, _ = self.self_attn(
+ hidden_states=hidden_states,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ position_embeddings=position_embeddings,
+ **kwargs,
+ )
+ hidden_states = residual + hidden_states
+
+ # Fully Connected
+ residual = hidden_states
+ hidden_states = self.post_attention_layernorm(hidden_states)
+ hidden_states = self.mlp(hidden_states)
+ hidden_states = residual + hidden_states
+ return hidden_states
+
+
+@auto_docstring
+class Glm4vMoePreTrainedModel(PreTrainedModel):
+ config: Glm4vMoeConfig
+ base_model_prefix = "model"
+ supports_gradient_checkpointing = True
+ _no_split_modules = ["Glm4vMoeTextDecoderLayer", "Glm4vMoeVisionBlock"]
+ _skip_keys_device_placement = "past_key_values"
+ _supports_flash_attn = True
+ _supports_sdpa = True
+ _supports_flex_attn = True
+
+ _can_compile_fullgraph = True
+ _supports_attention_backend = True
+ _can_record_outputs = {}
+ _keep_in_fp32_modules_strict = ["e_score_correction_bias"]
+ _keys_to_ignore_on_load_unexpected = [r"model\.layers\.92.*", r"model\.layers\.46.*"]
+ input_modalities = ("text", "image", "video")
+
+ @torch.no_grad()
+ def _init_weights(self, module):
+ super()._init_weights(module)
+ if isinstance(module, Glm4vMoeTextTopkRouter):
+ init.normal_(module.weight, mean=0.0, std=self.config.initializer_range)
+ init.zeros_(module.e_score_correction_bias)
+ elif isinstance(module, Glm4vMoeTextNaiveMoe):
+ init.normal_(module.gate_up_proj, mean=0.0, std=self.config.initializer_range)
+ init.normal_(module.down_proj, mean=0.0, std=self.config.initializer_range)
+ if isinstance(module, Glm4vMoeVisionRotaryEmbedding):
+ inv_freq = 1.0 / (module.theta ** (torch.arange(0, module.dim, 2, dtype=torch.float) / module.dim))
+ init.copy_(module.inv_freq, inv_freq)
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ Base class for Glm4vMoe causal language model (or autoregressive) outputs.
+ """
+)
+class Glm4vMoeCausalLMOutputWithPast(ModelOutput):
+ r"""
+ loss (`torch.FloatTensor` of shape `(1,)`, *optional*, returned when `labels` is provided):
+ Language modeling loss (for next-token prediction).
+ logits (`torch.FloatTensor` of shape `(batch_size, sequence_length, config.vocab_size)`):
+ Prediction scores of the language modeling head (scores for each vocabulary token before SoftMax).
+ past_key_values (`Cache`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
+ It is a [`~cache_utils.Cache`] instance. For more details, see our [kv cache guide](https://huggingface.co/docs/transformers/en/kv_cache).
+
+ Contains pre-computed hidden-states (key and values in the self-attention blocks) that can be used (see
+ `past_key_values` input) to speed up sequential decoding.
+ rope_deltas (`torch.LongTensor` of shape `(batch_size, )`, *optional*):
+ The rope index difference between sequence length and multimodal rope.
+ """
+
+ loss: torch.FloatTensor | None = None
+ logits: torch.FloatTensor | None = None
+ past_key_values: Cache | None = None
+ hidden_states: tuple[torch.FloatTensor] | None = None
+ attentions: tuple[torch.FloatTensor] | None = None
+ rope_deltas: torch.LongTensor | None = None
+ router_logits: tuple[torch.FloatTensor] | None = None
+ aux_loss: torch.FloatTensor | None = None
+
+
+class Glm4vMoeVisionRotaryEmbedding(nn.Module):
+ inv_freq: torch.Tensor # fix linting for `register_buffer`
+
+ def __init__(self, dim: int, theta: float = 10000.0) -> None:
+ super().__init__()
+ self.dim = dim
+ self.theta = theta
+ inv_freq = 1.0 / (theta ** (torch.arange(0, dim, 2, dtype=torch.float) / dim))
+ self.register_buffer("inv_freq", inv_freq, persistent=False)
+
+ def forward(self, seqlen: int) -> torch.Tensor:
+ seq = torch.arange(seqlen, device=self.inv_freq.device, dtype=self.inv_freq.dtype)
+ freqs = torch.outer(seq, self.inv_freq)
+ return freqs
+
+
+@use_kernel_forward_from_hub("RMSNorm")
+class Glm4vMoeRMSNorm(nn.Module):
+ def __init__(self, hidden_size, eps: float = 1e-6) -> None:
+ """
+ Glm4vMoeRMSNorm is equivalent to T5LayerNorm
+ """
+ super().__init__()
+ self.weight = nn.Parameter(torch.ones(hidden_size))
+ self.variance_epsilon = eps
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ input_dtype = hidden_states.dtype
+ hidden_states = hidden_states.to(torch.float32)
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
+ hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
+ return self.weight * hidden_states.to(input_dtype)
+
+ def extra_repr(self):
+ return f"{tuple(self.weight.shape)}, eps={self.variance_epsilon}"
+
+
+class Glm4vMoeisionMlp(nn.Module):
+ def __init__(self, config, bias: bool = False):
+ super().__init__()
+ self.hidden_size = config.hidden_size
+ self.intermediate_size = config.out_hidden_size
+ self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=bias)
+ self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=bias)
+ self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=bias)
+ self.act_fn = ACT2FN[config.hidden_act]
+
+ def forward(self, hidden_state):
+ return self.down_proj(self.act_fn(self.gate_proj(hidden_state)) * self.up_proj(hidden_state))
+
+
+class Glm4vMoeVisionPatchEmbed(nn.Module):
+ def __init__(self, config: Glm4vMoeVisionConfig) -> None:
+ super().__init__()
+ self.patch_size = config.patch_size
+ self.temporal_patch_size = config.temporal_patch_size
+ self.in_channels = config.in_channels
+ self.embed_dim = config.hidden_size
+
+ kernel_size = [self.temporal_patch_size, self.patch_size, self.patch_size]
+ self.proj = nn.Conv3d(self.in_channels, self.embed_dim, kernel_size=kernel_size, stride=kernel_size)
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ target_dtype = self.proj.weight.dtype
+ hidden_states = hidden_states.view(
+ -1, self.in_channels, self.temporal_patch_size, self.patch_size, self.patch_size
+ )
+ hidden_states = self.proj(hidden_states.to(dtype=target_dtype)).view(-1, self.embed_dim)
+ return hidden_states
+
+
+class Glm4vMoeVisionPatchMerger(nn.Module):
+ def __init__(self, dim: int, context_dim: int, hidden_act: str, bias: bool = False) -> None:
+ super().__init__()
+ self.proj = nn.Linear(dim, dim, bias=bias)
+ self.post_projection_norm = LayerNorm(dim)
+ self.gate_proj = nn.Linear(dim, context_dim, bias=bias)
+ self.up_proj = nn.Linear(dim, context_dim, bias=bias)
+ self.down_proj = nn.Linear(context_dim, dim, bias=bias)
+ self.act1 = nn.GELU()
+ self.act_fn = ACT2FN[hidden_act]
+
+ def forward(self, hidden_state: torch.Tensor) -> torch.Tensor:
+ hidden_state = self.proj(hidden_state)
+ hidden_state = self.act1(self.post_projection_norm(hidden_state))
+ return self.down_proj(self.act_fn(self.gate_proj(hidden_state)) * self.up_proj(hidden_state))
+
+
+class Glm4vMoeVisionEmbeddings(nn.Module):
+ def __init__(self, config: Glm4vMoeVisionConfig):
+ super().__init__()
+ self.config = config
+ self.embed_dim = config.hidden_size
+ self.image_size = config.image_size
+ self.patch_size = config.patch_size
+
+ self.num_patches = (self.image_size // self.patch_size) ** 2
+ self.num_positions = self.num_patches
+ self.position_embedding = nn.Embedding(self.num_positions, self.embed_dim)
+ self.interpolated_method = "bicubic"
+
+ def forward(self, embeddings, lengths, image_shapes, h_coords, w_coords) -> torch.Tensor:
+ """
+ Forward pass with integrated position encoding adaptation using 2D interpolation.
+
+ Args:
+ embeddings: Input embeddings tensor
+ lengths (torch.Tensor): Sequence lengths for each image in the batch.
+ image_shapes (torch.Tensor): Tensor of shape [batch_size, 3] representing the image shapes (t, h, w).
+ h_coords (torch.Tensor): Tensor of shape [total_seq] representing the h coordinate for each patch.
+ w_coords (torch.Tensor): Tensor of shape [total_seq] representing the w coordinate for each patch.
+
+ Returns:
+ torch.Tensor: Embeddings with adapted position encoding added.
+ """
+ # Get position embedding parameters
+ pos_embed_weight = self.position_embedding.weight
+ hidden_size = pos_embed_weight.shape[1]
+ device = pos_embed_weight.device
+
+ # Convert inputs to tensors if needed
+ if isinstance(lengths, list):
+ lengths = torch.tensor(lengths, device=device, dtype=torch.long)
+
+ # Prepare 2D position embedding
+ orig_size_sq = pos_embed_weight.shape[0]
+ orig_size = int(orig_size_sq**0.5)
+ pos_embed_2d = (
+ pos_embed_weight.view(orig_size, orig_size, hidden_size)
+ .permute(2, 0, 1)
+ .unsqueeze(0)
+ .to(device=device, dtype=torch.float32)
+ )
+
+ # Calculate target dimensions for each patch
+ target_h = torch.cat([image_shapes[i, 1].repeat(lengths[i]) for i in range(len(lengths))]).to(
+ device=device, dtype=torch.float32
+ )
+ target_w = torch.cat([image_shapes[i, 2].repeat(lengths[i]) for i in range(len(lengths))]).to(
+ device=device, dtype=torch.float32
+ )
+
+ # Normalize coordinates to [-1, 1] range for grid_sample
+ norm_w = ((w_coords + 0.5) / target_w) * 2 - 1
+ norm_h = ((h_coords + 0.5) / target_h) * 2 - 1
+
+ # Create sampling grid
+ grid = torch.stack((norm_w, norm_h), dim=-1).unsqueeze(0).unsqueeze(2)
+
+ # Perform bicubic interpolation
+ interpolated_embed_fp32 = F.grid_sample(
+ pos_embed_2d, grid, mode=self.interpolated_method, align_corners=False, padding_mode="border"
+ )
+
+ # Reshape and convert back to original dtype
+ adapted_pos_embed_fp32 = interpolated_embed_fp32.squeeze(0).squeeze(-1).permute(1, 0)
+ adapted_pos_embed = adapted_pos_embed_fp32.to(pos_embed_weight.dtype).to(embeddings.device)
+
+ # Add adapted position encoding to embeddings
+ embeddings = embeddings + adapted_pos_embed
+ return embeddings
+
+
+def apply_rotary_pos_emb_vision(
+ q: torch.Tensor, k: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor
+) -> tuple[torch.Tensor, torch.Tensor]:
+ orig_q_dtype = q.dtype
+ orig_k_dtype = k.dtype
+ q, k = q.float(), k.float()
+ cos, sin = cos.unsqueeze(-2).float(), sin.unsqueeze(-2).float()
+ q_embed = (q * cos) + (rotate_half(q) * sin)
+ k_embed = (k * cos) + (rotate_half(k) * sin)
+ q_embed = q_embed.to(orig_q_dtype)
+ k_embed = k_embed.to(orig_k_dtype)
+ return q_embed, k_embed
+
+
+class Glm4vMoeVisionAttention(nn.Module):
+ def __init__(self, config: Glm4vMoeVisionConfig) -> None:
+ super().__init__()
+ self.dim = config.hidden_size
+ self.num_heads = config.num_heads
+ self.head_dim = self.dim // self.num_heads
+ self.num_key_value_groups = 1 # needed for eager attention
+ self.qkv = nn.Linear(config.hidden_size, config.hidden_size * 3, bias=config.attention_bias)
+ self.proj = nn.Linear(config.hidden_size, config.hidden_size, bias=False)
+ self.scaling = self.head_dim**-0.5
+ self.config = config
+ self.attention_dropout = config.attention_dropout
+ self.is_causal = False
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ cu_seqlens: torch.Tensor,
+ rotary_pos_emb: torch.Tensor | None = None,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
+ **kwargs,
+ ) -> torch.Tensor:
+ seq_length = hidden_states.shape[0]
+ query_states, key_states, value_states = (
+ self.qkv(hidden_states).reshape(seq_length, 3, self.num_heads, -1).permute(1, 0, 2, 3).unbind(0)
+ )
+ cos, sin = position_embeddings
+ query_states, key_states = apply_rotary_pos_emb_vision(query_states, key_states, cos, sin)
+
+ query_states = query_states.transpose(0, 1).unsqueeze(0)
+ key_states = key_states.transpose(0, 1).unsqueeze(0)
+ value_states = value_states.transpose(0, 1).unsqueeze(0)
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ if is_flash_attention_requested(self.config):
+ # Flash Attention: Use cu_seqlens for variable length attention
+ max_seqlen = (cu_seqlens[1:] - cu_seqlens[:-1]).max()
+ attn_output, _ = attention_interface(
+ self,
+ query_states,
+ key_states,
+ value_states,
+ attention_mask=None,
+ scaling=self.scaling,
+ dropout=0.0 if not self.training else self.attention_dropout,
+ cu_seq_lens_q=cu_seqlens,
+ cu_seq_lens_k=cu_seqlens,
+ max_length_q=max_seqlen,
+ max_length_k=max_seqlen,
+ is_causal=False,
+ **kwargs,
+ )
+ else:
+ # Other implementations: Process each chunk separately
+ lengths = cu_seqlens[1:] - cu_seqlens[:-1]
+ splits = [
+ torch.split(tensor, lengths.tolist(), dim=2) for tensor in (query_states, key_states, value_states)
+ ]
+
+ attn_outputs = [
+ attention_interface(
+ self,
+ q,
+ k,
+ v,
+ attention_mask=None,
+ scaling=self.scaling,
+ dropout=0.0 if not self.training else self.attention_dropout,
+ is_causal=False,
+ **kwargs,
+ )[0]
+ for q, k, v in zip(*splits)
+ ]
+ attn_output = torch.cat(attn_outputs, dim=1)
+
+ attn_output = attn_output.reshape(seq_length, -1).contiguous()
+ attn_output = self.proj(attn_output)
+ return attn_output
+
+
+class Glm4vMoeVisionBlock(GradientCheckpointingLayer):
+ def __init__(self, config) -> None:
+ super().__init__()
+ self.norm1 = Glm4vMoeRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.norm2 = Glm4vMoeRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.attn = Glm4vMoeVisionAttention(config)
+ self.mlp = Glm4vMoeisionMlp(config, bias=False)
+
+ @auto_docstring
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ cu_seqlens: torch.Tensor,
+ rotary_pos_emb: torch.Tensor | None = None,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
+ **kwargs,
+ ) -> torch.Tensor:
+ r"""
+ cu_seqlens (`torch.Tensor`):
+ Cumulative sequence lengths used for packed variable-length attention in Flash Attention kernels.
+ rotary_pos_emb (`torch.Tensor`, *optional*):
+ Precomputed rotary positional embeddings applied to the vision attention query/key states.
+ """
+ hidden_states = hidden_states + self.attn(
+ self.norm1(hidden_states),
+ cu_seqlens=cu_seqlens,
+ rotary_pos_emb=rotary_pos_emb,
+ position_embeddings=position_embeddings,
+ **kwargs,
+ )
+ hidden_states = hidden_states + self.mlp(self.norm2(hidden_states))
+ return hidden_states
+
+
+@auto_docstring
+class Glm4vMoeVisionModel(Glm4vMoePreTrainedModel):
+ config: Glm4vMoeVisionConfig
+ input_modalities = ("image", "video")
+ _no_split_modules = ["Glm4vMoeVisionBlock"]
+ _can_record_outputs = {
+ "hidden_states": Glm4vMoeVisionBlock,
+ "attentions": Glm4vMoeVisionAttention,
+ }
+
+ def __init__(self, config) -> None:
+ super().__init__(config)
+ self.spatial_merge_size = config.spatial_merge_size
+ self.patch_size = config.patch_size
+
+ self.embeddings = Glm4vMoeVisionEmbeddings(config)
+ self.patch_embed = Glm4vMoeVisionPatchEmbed(config)
+
+ head_dim = config.hidden_size // config.num_heads
+ self.rotary_pos_emb = Glm4vMoeVisionRotaryEmbedding(head_dim // 2)
+
+ self.blocks = nn.ModuleList([Glm4vMoeVisionBlock(config) for _ in range(config.depth)])
+ self.merger = Glm4vMoeVisionPatchMerger(
+ dim=config.out_hidden_size, context_dim=config.intermediate_size, hidden_act=config.hidden_act
+ )
+
+ self.post_conv_layernorm = Glm4vMoeRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.downsample = nn.Conv2d(
+ in_channels=config.hidden_size,
+ out_channels=config.out_hidden_size,
+ kernel_size=config.spatial_merge_size,
+ stride=config.spatial_merge_size,
+ )
+ self.post_layernorm = Glm4vMoeRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+
+ self.gradient_checkpointing = False
+ self.post_init()
+
+ def rot_pos_emb(self, grid_thw):
+ pos_ids = []
+ for t, h, w in grid_thw:
+ hpos_ids = torch.arange(h).unsqueeze(1).expand(-1, w)
+ hpos_ids = hpos_ids.reshape(
+ h // self.spatial_merge_size,
+ self.spatial_merge_size,
+ w // self.spatial_merge_size,
+ self.spatial_merge_size,
+ )
+ hpos_ids = hpos_ids.permute(0, 2, 1, 3)
+ hpos_ids = hpos_ids.flatten()
+
+ wpos_ids = torch.arange(w).unsqueeze(0).expand(h, -1)
+ wpos_ids = wpos_ids.reshape(
+ h // self.spatial_merge_size,
+ self.spatial_merge_size,
+ w // self.spatial_merge_size,
+ self.spatial_merge_size,
+ )
+ wpos_ids = wpos_ids.permute(0, 2, 1, 3)
+ wpos_ids = wpos_ids.flatten()
+ pos_ids.append(torch.stack([hpos_ids, wpos_ids], dim=-1).repeat(t, 1))
+ pos_ids = torch.cat(pos_ids, dim=0)
+ max_grid_size = grid_thw[:, 1:].max()
+ rotary_pos_emb_full = self.rotary_pos_emb(max_grid_size)
+ rotary_pos_emb = rotary_pos_emb_full[pos_ids].flatten(1)
+ return rotary_pos_emb, pos_ids
+
+ @merge_with_config_defaults
+ @capture_outputs
+ @auto_docstring
+ def forward(
+ self, hidden_states: torch.Tensor, grid_thw: torch.Tensor, **kwargs: Unpack[TransformersKwargs]
+ ) -> tuple | BaseModelOutputWithPooling:
+ r"""
+ hidden_states (`torch.Tensor` of shape `(seq_len, hidden_size)`):
+ The final hidden states of the model.
+ grid_thw (`torch.Tensor` of shape `(num_images_or_videos, 3)`):
+ The temporal, height and width of feature shape of each image in LLM.
+
+ Returns:
+ `torch.Tensor`: hidden_states.
+ """
+ hidden_states = self.patch_embed(hidden_states)
+ hidden_states = self.post_conv_layernorm(hidden_states)
+ rotary_pos_emb, image_type_ids = self.rot_pos_emb(grid_thw)
+ emb = torch.cat((rotary_pos_emb, rotary_pos_emb), dim=-1)
+ position_embeddings = (emb.cos(), emb.sin())
+
+ cu_seqlens = torch.repeat_interleave(grid_thw[:, 1] * grid_thw[:, 2], grid_thw[:, 0]).cumsum(
+ dim=0,
+ # Select dtype based on the following factors:
+ # - FA2 requires that cu_seqlens_q must have dtype int32
+ # - torch.onnx.export requires that cu_seqlens_q must have same dtype as grid_thw
+ # See https://github.com/huggingface/transformers/pull/34852 for more information
+ dtype=grid_thw.dtype if torch.jit.is_tracing() else torch.int32,
+ )
+ cu_seqlens = F.pad(cu_seqlens, (1, 0), value=0)
+ seqlens = (cu_seqlens[1:] - cu_seqlens[:-1]).tolist()
+ hidden_states = self.embeddings(
+ hidden_states,
+ seqlens,
+ grid_thw,
+ image_type_ids[:, 0].to(hidden_states.device),
+ image_type_ids[:, 1].to(hidden_states.device),
+ )
+
+ for blk in self.blocks:
+ hidden_states = blk(
+ hidden_states,
+ cu_seqlens=cu_seqlens,
+ position_embeddings=position_embeddings,
+ **kwargs,
+ )
+
+ hidden_states = self.post_layernorm(hidden_states)
+
+ hidden_states = hidden_states.view(
+ -1, self.spatial_merge_size, self.spatial_merge_size, hidden_states.shape[-1]
+ )
+ hidden_states = hidden_states.permute(0, 3, 1, 2)
+ hidden_states = self.downsample(hidden_states).view(-1, self.config.out_hidden_size)
+
+ merged_hidden_states = self.merger(hidden_states)
+
+ return BaseModelOutputWithPooling(
+ last_hidden_state=hidden_states,
+ pooler_output=merged_hidden_states,
+ )
+
+
+class Glm4vMoeTextRotaryEmbedding(nn.Module):
+ inv_freq: torch.Tensor # fix linting for `register_buffer`
+
+ def __init__(self, config: Glm4vMoeTextConfig, device=None):
+ super().__init__()
+ self.max_seq_len_cached = config.max_position_embeddings
+ self.original_max_seq_len = config.max_position_embeddings
+
+ self.config = config
+
+ self.rope_type = self.config.rope_parameters["rope_type"]
+ rope_init_fn: Callable = self.compute_default_rope_parameters
+ if self.rope_type != "default":
+ rope_init_fn = ROPE_INIT_FUNCTIONS[self.rope_type]
+ inv_freq, self.attention_scaling = rope_init_fn(self.config, device)
+
+ self.register_buffer("inv_freq", inv_freq, persistent=False)
+ self.register_buffer("original_inv_freq", inv_freq.clone(), persistent=False)
+ self.mrope_section = config.rope_parameters.get("mrope_section", [8, 12, 12])
+
+ @staticmethod
+ def compute_default_rope_parameters(
+ config: Glm4vMoeTextConfig | None = None,
+ device: Optional["torch.device"] = None,
+ seq_len: int | None = None,
+ ) -> tuple["torch.Tensor", float]:
+ """
+ Computes the inverse frequencies according to the original RoPE implementation
+ Args:
+ config ([`~transformers.PreTrainedConfig`]):
+ The model configuration.
+ device (`torch.device`):
+ The device to use for initialization of the inverse frequencies.
+ seq_len (`int`, *optional*):
+ The current sequence length. Unused for this type of RoPE.
+ Returns:
+ Tuple of (`torch.Tensor`, `float`), containing the inverse frequencies for the RoPE embeddings and the
+ post-processing scaling factor applied to the computed cos/sin (unused in this type of RoPE).
+ """
+ base = config.rope_parameters["rope_theta"]
+ partial_rotary_factor = config.rope_parameters.get("partial_rotary_factor", 1.0)
+ head_dim = getattr(config, "head_dim", None) or config.hidden_size // config.num_attention_heads
+ dim = int(head_dim * partial_rotary_factor)
+
+ attention_factor = 1.0 # Unused in this type of RoPE
+
+ # Compute the inverse frequencies
+ inv_freq = 1.0 / (
+ base ** (torch.arange(0, dim, 2, dtype=torch.int64).to(device=device, dtype=torch.float) / dim)
+ )
+ return inv_freq, attention_factor
+
+ @torch.no_grad()
+ @dynamic_rope_update # power user: used with advanced RoPE types (e.g. dynamic rope)
+ def forward(self, x, position_ids):
+ # In contrast to other models, GLM-V has different position ids for the grids
+ # So we expand the inv_freq to shape (3, ...)
+ inv_freq_expanded = self.inv_freq[None, None, :, None].float().expand(3, position_ids.shape[1], -1, 1)
+ position_ids_expanded = position_ids[:, :, None, :].float() # shape (3, bs, 1, positions)
+
+ device_type = x.device.type if isinstance(x.device.type, str) and x.device.type != "mps" else "cpu"
+ with maybe_autocast(device_type=device_type, enabled=False): # Force float32
+ freqs = (inv_freq_expanded.float() @ position_ids_expanded.float()).transpose(2, 3)
+ freqs = self.apply_mrope(freqs, self.mrope_section)
+ emb = torch.cat((freqs, freqs), dim=-1)
+ cos = emb.cos() * self.attention_scaling
+ sin = emb.sin() * self.attention_scaling
+
+ return cos.to(dtype=x.dtype), sin.to(dtype=x.dtype)
+
+ def apply_mrope(self, freqs, mrope_section):
+ section = mrope_section
+ chunks = freqs.split(section, dim=-1)
+ result = torch.cat([chunk[i % 3] for i, chunk in enumerate(chunks)], dim=-1)
+ return result
+
+
+@auto_docstring
+class Glm4vMoeTextModel(Glm4vMoePreTrainedModel):
+ config: Glm4vMoeTextConfig
+ input_modalities = ("text",)
+ _can_record_outputs = {
+ "hidden_states": Glm4vMoeTextDecoderLayer,
+ "attentions": Glm4vMoeTextAttention,
+ "router_logits": Glm4vMoeTextTopkRouter,
+ }
+
+ def __init__(self, config: Glm4vMoeTextConfig):
+ super().__init__(config)
+ self.padding_idx = config.pad_token_id
+ self.vocab_size = config.vocab_size
+
+ self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)
+ self.layers = nn.ModuleList(
+ [Glm4vMoeTextDecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
+ )
+ self.norm = Glm4vMoeRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.rotary_emb = Glm4vMoeTextRotaryEmbedding(config=config)
+
+ self.gradient_checkpointing = False
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @auto_docstring
+ @merge_with_config_defaults
+ @capture_outputs
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ use_cache: bool | None = None,
+ **kwargs: Unpack[FlashAttentionKwargs],
+ ) -> tuple | MoeModelOutputWithPast:
+ if (input_ids is None) ^ (inputs_embeds is not None):
+ raise ValueError("You must specify exactly one of input_ids or inputs_embeds")
+
+ # torch.jit.trace() doesn't support cache objects in the output
+ if use_cache and past_key_values is None and not torch.jit.is_tracing():
+ past_key_values = DynamicCache(config=self.config)
+
+ if inputs_embeds is None:
+ inputs_embeds = self.embed_tokens(input_ids)
+
+ # the hard coded `3` is for temporal, height and width.
+ if position_ids is None:
+ past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
+ position_ids = torch.arange(inputs_embeds.shape[1], device=inputs_embeds.device) + past_seen_tokens
+ position_ids = position_ids.view(1, 1, -1).expand(3, inputs_embeds.shape[0], -1)
+ elif position_ids.ndim == 2:
+ position_ids = position_ids[None, ...].expand(3, position_ids.shape[0], -1)
+
+ # NOTE: we need to pass text position ids for packing. Qwen2-VL uses 3D positions
+ # where each dim indicates visual spatial positions for temporal/height/width grids.
+ # There are two scenarios when FA2-like packed masking might be activated.
+ # 1. User specifically passed packed `position_ids` and no attention mask.
+ # In this case we expect the useer to create correct position ids for all 3 grids
+ # and prepend text-only position ids to it. The final tensor will be [4, bs, seq-len]
+ # 2. User runs forward with no attention mask and no position ids. In this case, position ids
+ # are prepared by the model (`get_rope_index`) as `[4, bs, seq-len]` tensor. Text-only positions are
+ # prepended by us when creating positions so that the mask is constructed correctly. NOTE: failing to pass
+ # text-only positions will cause incorrect mask construction, do not change `prepare_input_for_generation`
+ if position_ids.ndim == 3 and position_ids.shape[0] == 4:
+ text_position_ids = position_ids[0]
+ position_ids = position_ids[1:]
+ else:
+ # If inputs are not packed (usual 3D positions), do not prepare mask from position_ids
+ text_position_ids = None
+
+ mask_kwargs = {
+ "config": self.config,
+ "inputs_embeds": inputs_embeds,
+ "attention_mask": attention_mask,
+ "past_key_values": past_key_values,
+ "position_ids": text_position_ids,
+ }
+ # Create the masks
+ causal_mask = create_causal_mask(**mask_kwargs)
+
+ hidden_states = inputs_embeds
+
+ # create position embeddings to be shared across the decoder layers
+ position_embeddings = self.rotary_emb(hidden_states, position_ids)
+
+ for i, decoder_layer in enumerate(self.layers[: self.config.num_hidden_layers]):
+ layer_outputs = decoder_layer(
+ hidden_states,
+ position_embeddings=position_embeddings,
+ attention_mask=causal_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ **kwargs,
+ )
+ hidden_states = layer_outputs
+
+ hidden_states = self.norm(hidden_states)
+
+ return MoeModelOutputWithPast(
+ last_hidden_state=hidden_states,
+ past_key_values=past_key_values,
+ )
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ Base class for Llava outputs, with hidden states and attentions.
+ """
+)
+class Glm4vMoeModelOutputWithPast(ModelOutput):
+ r"""
+ past_key_values (`Cache`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
+ It is a [`~cache_utils.Cache`] instance. For more details, see our [kv cache guide](https://huggingface.co/docs/transformers/en/kv_cache).
+
+ Contains pre-computed hidden-states (key and values in the self-attention blocks) that can be used (see
+ `past_key_values` input) to speed up sequential decoding.
+ rope_deltas (`torch.LongTensor` of shape `(batch_size, )`, *optional*):
+ The rope index difference between sequence length and multimodal rope.
+ """
+
+ last_hidden_state: torch.FloatTensor | None = None
+ past_key_values: Cache | None = None
+ hidden_states: tuple[torch.FloatTensor] | None = None
+ attentions: tuple[torch.FloatTensor] | None = None
+ rope_deltas: torch.LongTensor | None = None
+ router_logits: tuple[torch.FloatTensor] | None = None
+
+
+@auto_docstring
+class Glm4vMoeModel(Glm4vMoePreTrainedModel):
+ base_model_prefix = "model"
+ # Reference: fix gemma3 grad acc #37208
+ accepts_loss_kwargs = False
+ _no_split_modules = ["Glm4vMoeTextDecoderLayer", "Glm4vMoeVisionBlock"]
+
+ def __init__(self, config):
+ super().__init__(config)
+ self.visual = Glm4vMoeVisionModel._from_config(config.vision_config)
+ self.language_model = Glm4vMoeTextModel._from_config(config.text_config)
+ self.rope_deltas = None # cache rope_deltas here
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ def get_input_embeddings(self):
+ return self.language_model.get_input_embeddings()
+
+ def set_input_embeddings(self, value):
+ self.language_model.set_input_embeddings(value)
+
+ def get_vision_position_ids(
+ self,
+ start_position: int,
+ grid_thw: list[int, int, int] | torch.Tensor,
+ temp_merge_size: int = 1,
+ spatial_merge_size: int = 1,
+ time_interval: int = 1,
+ device: str | torch.device | None = None,
+ ):
+ """
+ Compute 3D positional indices for vision tokens derived from a single image or video input.
+
+ The positions are generated from the input grid defined by temporal (T), height (H), and
+ width (W) dimensions. Temporal and spatial dimensions can be downscaled according to the
+ merge sizes used in the vision backbone. The resulting positions are offset by `start_position`.
+
+ Args:
+ start_position (`int`):
+ Offset added to all computed positional indices.
+ grid_thw (`Sequence[int]` or `torch.Tensor` of shape `(3,)`):
+ The (T, H, W) grid representing the feature layout of the current image or video after patch embedding.
+ temp_merge_size (`int`, *optional*):
+ Factor by which the temporal dimension is reduced in the backbone. The temporal grid size is divided
+ by this value. Defaults to 1.
+ spatial_merge_size (`int`, *optional*):
+ Factor by which the spatial dimensions (H and W) are reduced in the backbone. Both H and W are divided
+ by this value. Defaults to 1.
+ time_interval (`int`, *optional*):
+ Spacing factor applied between consecutive temporal position indices.Defaults to 1.
+ device (`str` or `torch.device`, *optional*):
+ Device on which the resulting tensor is allocated. If `None`, uses the current default device.
+
+ Returns:
+ torch.LongTensor of shape (3, sequence_length):
+ Positional indices for temporal, height, and width dimensions,
+ flattened into sequence form and offset by `start_position`.
+ """
+ llm_grid_t, llm_grid_h, llm_grid_w = (
+ grid_thw[0].item() // temp_merge_size,
+ grid_thw[1].item() // spatial_merge_size,
+ grid_thw[2].item() // spatial_merge_size,
+ )
+
+ image_seq_length = llm_grid_h * llm_grid_w * llm_grid_t
+ position_width = torch.arange(start_position, start_position + llm_grid_w, device=device).repeat(
+ llm_grid_h * llm_grid_t
+ )
+ position_height = torch.arange(start_position, start_position + llm_grid_h, device=device).repeat_interleave(
+ llm_grid_w * llm_grid_t
+ )
+ position_temporal = torch.full((image_seq_length,), start_position, device=device, dtype=torch.long)
+ position_temporal = position_temporal * time_interval
+ vision_position_ids = torch.stack([position_temporal, position_height, position_width], dim=0)
+
+ return vision_position_ids
+
+ def get_rope_index(
+ self,
+ input_ids: torch.LongTensor,
+ mm_token_type_ids: torch.IntTensor,
+ image_grid_thw: torch.LongTensor | None = None,
+ video_grid_thw: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ **kwargs,
+ ) -> tuple[torch.Tensor, torch.Tensor]:
+ """
+ Calculate the 3D rope index based on image and video's sizes. The utility expects a `vision + text`
+ sequence and will error out otherwise. For pure text sequence, please rely on model's auto-inferred
+ position ids. In a mixed vision + text sequence, vision tokens use 3D RoPE (temporal, height, width)
+ while text tokens use standard 1D RoPE.
+
+ Example:
+ Temporal patches: 3; Height patches: 2; Width patches: 2
+ Each vision input results in (temporal x height × width) positions. Here: 3 x 2 × 2 = 12 positions total.
+
+ Temporal position IDs are spaced by:
+ `interval = tokens_per_second * temporal_patch_size / fps`
+
+ If fps = 1; tokens_per_second = 25; temporal_patch_size = 2, temporal IDs increase by 50 for each temporal patch:
+ `[0, 0, 0, 0, 50, 50, 50, 50, 100, 100, 100, 100]`
+
+ Height IDs repeat per row: `[0, 0, 1, 1, ...]`
+ Width IDs alternate per column: `[0, 1, 0, 1, ...]`
+ Text tokens follow standard 1D RoPE and the position IDs grow consequently with a step of `1`
+
+ Args:
+ input_ids (`torch.LongTensor` of shape `(batch_size, sequence_length)`):
+ Indices of input sequence tokens in the vocabulary. Padding will be ignored by default should you provide
+ it.
+ mm_token_type_ids (`torch.IntTensor` of shape `(batch_size, sequence_length)`):
+ Token type ids matching each modality to a different value in the input sequence, i.e. text (0), image (1), video (2).
+ 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.
+ attention_mask (`torch.Tensor` of shape `(batch_size, sequence_length)`, *optional*):
+ Mask to avoid performing attention on padding token indices. Mask values selected in `[0, 1]`:
+
+ - 1 for tokens that are **not masked**,
+ - 0 for tokens that are **masked**.
+
+ Returns:
+ position_ids (`torch.LongTensor` of shape `(3, batch_size, sequence_length)`)
+ mrope_position_deltas (`torch.Tensor` of shape `(batch_size)`)
+ """
+ spatial_merge_size = self.config.vision_config.spatial_merge_size
+
+ mrope_position_deltas = []
+ position_ids = torch.zeros(
+ 3,
+ input_ids.shape[0],
+ input_ids.shape[1],
+ dtype=input_ids.dtype,
+ device=input_ids.device,
+ )
+ grid_iters = {
+ 1: iter(image_grid_thw) if image_grid_thw is not None else None,
+ 2: iter(video_grid_thw) if video_grid_thw is not None else None,
+ }
+
+ for batch_idx, current_input_ids in enumerate(input_ids):
+ input_token_type = mm_token_type_ids[batch_idx]
+ if attention_mask is not None:
+ current_input_ids = current_input_ids[attention_mask[batch_idx].bool()]
+ input_token_type = input_token_type[attention_mask[batch_idx].bool()]
+
+ input_type_group = []
+ for key, group in itertools.groupby(enumerate(input_token_type.tolist()), lambda x: x[1]):
+ group = list(group)
+ start_index = group[0][0]
+ end_index = group[-1][0] + 1
+ input_type_group.append((key, start_index, end_index))
+
+ current_pos = 0
+ video_group_index = 0
+ llm_pos_ids_list = []
+ for modality_type, start_idx, end_idx in input_type_group:
+ # text == 0
+ if modality_type == 0:
+ text_len = end_idx - start_idx
+ llm_pos_ids_list.append(
+ torch.arange(text_len, device=input_ids.device).view(1, -1).expand(3, -1) + current_pos
+ )
+ current_pos += text_len
+ # image == 1, video == 2
+ else:
+ # GLM4V_MOE splits video into segments per frame but there's only one `grid_thw`
+ # per whole video. We can't exhaus the iterator and have to re-use the grid
+ # while processing the same video!
+ if modality_type == 2:
+ if video_group_index == 0:
+ grid_thw = next(grid_iters[modality_type])
+ video_group_index += 1
+ video_group_index = 0 if video_group_index >= grid_thw[0] else video_group_index
+ else:
+ grid_thw = next(grid_iters[modality_type])
+
+ # Videos are processed per frame separately, each temporal grid is always `1`
+ temp_merge_size = grid_thw[0]
+ vision_position_ids = self.get_vision_position_ids(
+ current_pos, grid_thw, temp_merge_size, spatial_merge_size, device=input_ids.device
+ )
+ llm_pos_ids_list.append(vision_position_ids)
+ current_pos += max(grid_thw[1], grid_thw[2]) // spatial_merge_size
+ llm_positions = torch.cat(llm_pos_ids_list, dim=1).reshape(3, -1)
+ if attention_mask is not None:
+ position_ids[:, batch_idx, attention_mask[batch_idx].bool()] = llm_positions.to(position_ids.device)
+ else:
+ position_ids[:, batch_idx] = llm_positions.to(position_ids.device)
+ mrope_position_deltas.append(llm_positions.max() + 1 - len(current_input_ids))
+ mrope_position_deltas = torch.tensor(mrope_position_deltas, device=input_ids.device).unsqueeze(1)
+ return position_ids, mrope_position_deltas
+
+ @can_return_tuple
+ @auto_docstring
+ def get_video_features(
+ self,
+ pixel_values_videos: torch.FloatTensor,
+ video_grid_thw: torch.LongTensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | BaseModelOutputWithPooling:
+ r"""
+ pixel_values_videos (`torch.FloatTensor` of shape `(batch_size, num_channels, image_size, image_size)`):
+ The tensors corresponding to the input videos.
+ video_grid_thw (`torch.LongTensor` of shape `(num_videos, 3)`, *optional*):
+ The temporal, height and width of feature shape of each video in LLM.
+ """
+ pixel_values_videos = pixel_values_videos.type(self.visual.dtype)
+ # reshape video_grid_thw -> [b, 3] -> [1, h, w] * frames
+ temp_frames_hw = []
+ video_grid_thw_list = video_grid_thw.tolist()
+ for t, h, w in video_grid_thw_list:
+ repeated_row = torch.tensor([1, h, w]).unsqueeze(0).repeat(t, 1)
+ temp_frames_hw.append(repeated_row)
+ flattened_video_grid_thw = torch.cat(temp_frames_hw, dim=0)
+ vision_outputs = self.visual(
+ pixel_values_videos, grid_thw=flattened_video_grid_thw, return_dict=True, **kwargs
+ )
+ split_sizes = (video_grid_thw.prod(-1) // self.visual.spatial_merge_size**2).tolist()
+ video_embeds = torch.split(vision_outputs.pooler_output, split_sizes)
+ vision_outputs.pooler_output = video_embeds
+
+ return vision_outputs
+
+ @can_return_tuple
+ @auto_docstring
+ def get_image_features(
+ self,
+ pixel_values: torch.FloatTensor,
+ image_grid_thw: torch.LongTensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | BaseModelOutputWithPooling:
+ r"""
+ pixel_values (`torch.FloatTensor` of shape `(batch_size, num_channels, image_size, image_size)`):
+ The tensors corresponding to the input images.
+ image_grid_thw (`torch.LongTensor` of shape `(num_images, 3)`, *optional*):
+ The temporal, height and width of feature shape of each image in LLM.
+ """
+ pixel_values = pixel_values.type(self.visual.dtype)
+ vision_outputs = self.visual(pixel_values, grid_thw=image_grid_thw, **kwargs)
+ split_sizes = (image_grid_thw.prod(-1) // self.visual.spatial_merge_size**2).tolist()
+ image_embeds = torch.split(vision_outputs.pooler_output, split_sizes)
+ vision_outputs.pooler_output = image_embeds
+
+ return vision_outputs
+
+ def get_placeholder_mask(
+ self,
+ input_ids: torch.LongTensor,
+ inputs_embeds: torch.FloatTensor,
+ image_features: torch.FloatTensor | None = None,
+ video_features: torch.FloatTensor | None = None,
+ ):
+ """
+ Obtains multimodal placeholder mask from `input_ids` or `inputs_embeds`, and checks that the placeholder token count is
+ equal to the length of multimodal features. If the lengths are different, an error is raised.
+ """
+ if input_ids is None:
+ special_image_mask = inputs_embeds == self.get_input_embeddings()(
+ torch.tensor(self.config.image_token_id, dtype=torch.long, device=inputs_embeds.device)
+ )
+ special_image_mask = special_image_mask.all(-1)
+ special_video_mask = inputs_embeds == self.get_input_embeddings()(
+ torch.tensor(self.config.video_token_id, dtype=torch.long, device=inputs_embeds.device)
+ )
+ special_video_mask = special_video_mask.all(-1)
+ else:
+ # GLM-4.1V and GLM-4.5V special_video_mask is special_image_mask
+ special_image_mask = input_ids == self.config.image_token_id
+ special_video_mask = input_ids == self.config.image_token_id
+
+ n_image_tokens = special_image_mask.sum()
+ special_image_mask = special_image_mask.unsqueeze(-1).expand_as(inputs_embeds).to(inputs_embeds.device)
+ if image_features is not None:
+ torch_compilable_check(
+ inputs_embeds[special_image_mask].numel() == image_features.numel(),
+ f"Image features and image tokens do not match, tokens: {n_image_tokens}, features: {image_features.shape[0]}",
+ )
+
+ n_video_tokens = special_video_mask.sum()
+ special_video_mask = special_video_mask.unsqueeze(-1).expand_as(inputs_embeds).to(inputs_embeds.device)
+ if video_features is not None:
+ torch_compilable_check(
+ inputs_embeds[special_video_mask].numel() == video_features.numel(),
+ f"Video features and video tokens do not match, tokens: {n_video_tokens}, features: {video_features.shape[0]}",
+ )
+ return special_image_mask, special_video_mask
+
+ def compute_3d_position_ids(
+ self,
+ input_ids: torch.Tensor | None,
+ inputs_embeds: torch.Tensor | None,
+ image_grid_thw: torch.Tensor | None = None,
+ video_grid_thw: torch.Tensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ past_key_values: torch.Tensor | None = None,
+ mm_token_type_ids: torch.IntTensor | None = None,
+ ) -> torch.Tensor | None:
+ past_key_values_length = 0 if past_key_values is None else past_key_values.get_seq_length()
+ has_multimodal = image_grid_thw is not None or video_grid_thw is not None
+ if has_multimodal and mm_token_type_ids is None and input_ids is not None:
+ raise ValueError(
+ "Multimodal data was passed (via `image_grid_thw` or `video_grid_thw`) but `mm_token_type_ids` is "
+ "missing. Please pass `mm_token_type_ids` to the model so that multimodal RoPE (M-RoPE) can be "
+ "computed correctly. `mm_token_type_ids` is returned by the processor alongside `input_ids`."
+ )
+ can_compute_mrope = input_ids is not None and mm_token_type_ids is not None and has_multimodal
+
+ if can_compute_mrope and (self.rope_deltas is None or past_key_values_length == 0):
+ position_ids, rope_deltas = self.get_rope_index(
+ input_ids,
+ image_grid_thw=image_grid_thw,
+ video_grid_thw=video_grid_thw,
+ attention_mask=attention_mask,
+ mm_token_type_ids=mm_token_type_ids,
+ )
+ self.rope_deltas = rope_deltas
+ # Use pre-calculated rope-deltas to infer correct 3D position ids during incremental
+ # generation (past_key_values_length > 0) or when only inputs_embeds is provided (no input_ids
+ # to recompute from). Skip when input_ids is provided without past_key_values to avoid shape
+ # mismatches from stale rope_deltas (e.g., training forward pass after generation).
+ elif self.rope_deltas is not None and (past_key_values_length > 0 or input_ids is None):
+ batch_size, seq_length, _ = inputs_embeds.shape
+ if attention_mask is not None:
+ position_ids = attention_mask.long().cumsum(-1) - 1
+ position_ids = position_ids.masked_fill(attention_mask == 0, 0)
+ position_ids = position_ids.view(1, batch_size, -1).repeat(3, 1, 1).to(inputs_embeds.device)
+ else:
+ position_ids = torch.arange(past_key_values_length, past_key_values_length + seq_length)
+ position_ids = position_ids.view(1, 1, -1).expand(3, batch_size, -1).to(inputs_embeds.device)
+ delta = self.rope_deltas.repeat_interleave(batch_size // self.rope_deltas.shape[0], dim=0)
+ position_ids = position_ids + delta.to(device=inputs_embeds.device)
+ else:
+ # Can't build correct 3D positions. Let the model infer it
+ position_ids = None
+ return position_ids
+
+ @auto_docstring
+ @can_return_tuple
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ pixel_values: torch.Tensor | None = None,
+ pixel_values_videos: torch.FloatTensor | None = None,
+ image_grid_thw: torch.LongTensor | None = None,
+ video_grid_thw: torch.LongTensor | None = None,
+ rope_deltas: torch.LongTensor | None = None,
+ mm_token_type_ids: torch.IntTensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | Glm4vMoeModelOutputWithPast:
+ r"""
+ 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.
+ """
+ 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 None:
+ inputs_embeds = self.get_input_embeddings()(input_ids)
+
+ if pixel_values is not None:
+ image_embeds = self.get_image_features(pixel_values, image_grid_thw, return_dict=True).pooler_output
+ image_embeds = torch.cat(image_embeds, dim=0).to(inputs_embeds.device, inputs_embeds.dtype)
+ image_mask, _ = self.get_placeholder_mask(input_ids, inputs_embeds, image_features=image_embeds)
+ inputs_embeds = inputs_embeds.masked_scatter(image_mask, image_embeds)
+
+ if pixel_values_videos is not None:
+ video_embeds = self.get_video_features(pixel_values_videos, video_grid_thw, return_dict=True).pooler_output
+ video_embeds = torch.cat(video_embeds, dim=0).to(inputs_embeds.device, inputs_embeds.dtype)
+ _, video_mask = self.get_placeholder_mask(input_ids, inputs_embeds, video_features=video_embeds)
+ inputs_embeds = inputs_embeds.masked_scatter(video_mask, video_embeds)
+
+ if position_ids is None:
+ position_ids = self.compute_3d_position_ids(
+ input_ids=input_ids,
+ image_grid_thw=image_grid_thw,
+ video_grid_thw=video_grid_thw,
+ inputs_embeds=inputs_embeds,
+ attention_mask=attention_mask,
+ past_key_values=past_key_values,
+ mm_token_type_ids=mm_token_type_ids,
+ )
+
+ outputs = self.language_model(
+ input_ids=None,
+ position_ids=position_ids,
+ attention_mask=attention_mask,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ **kwargs,
+ )
+
+ return Glm4vMoeModelOutputWithPast(
+ **outputs,
+ rope_deltas=self.rope_deltas,
+ )
+
+
+def load_balancing_loss_func(
+ gate_logits: torch.Tensor | tuple[torch.Tensor] | None,
+ num_experts: int | None = None,
+ top_k=2,
+ attention_mask: torch.Tensor | None = None,
+) -> torch.Tensor | int:
+ r"""
+ Computes auxiliary load balancing loss as in Switch Transformer - implemented in Pytorch.
+
+ See Switch Transformer (https://huggingface.co/papers/2101.03961) for more details. This function implements the loss
+ function presented in equations (4) - (6) of the paper. It aims at penalizing cases where the routing between
+ experts is too unbalanced.
+
+ Args:
+ gate_logits:
+ Logits from the `gate`, should be a tuple of model.config.num_hidden_layers tensors of
+ shape [batch_size X sequence_length, num_experts].
+ num_experts:
+ Number of experts
+ top_k:
+ The number of experts to route per-token, can be also interpreted as the `top-k` routing
+ parameter.
+ attention_mask (`torch.Tensor`, *optional*):
+ The attention_mask used in forward function
+ shape [batch_size X sequence_length] if not None.
+
+ Returns:
+ The auxiliary loss.
+ """
+ if gate_logits is None or not isinstance(gate_logits, tuple):
+ return 0
+
+ if isinstance(gate_logits, tuple):
+ compute_device = gate_logits[0].device
+ concatenated_gate_logits = torch.cat([layer_gate.to(compute_device) for layer_gate in gate_logits], dim=0)
+
+ routing_weights = torch.nn.functional.softmax(concatenated_gate_logits, dim=-1)
+
+ _, selected_experts = torch.topk(routing_weights, top_k, dim=-1)
+
+ expert_mask = torch.nn.functional.one_hot(selected_experts, num_experts)
+
+ if attention_mask is None:
+ # Compute the percentage of tokens routed to each experts
+ tokens_per_expert = torch.mean(expert_mask.float(), dim=0)
+
+ # Compute the average probability of routing to these experts
+ router_prob_per_expert = torch.mean(routing_weights, dim=0)
+ else:
+ batch_size, sequence_length = attention_mask.shape
+ num_hidden_layers = concatenated_gate_logits.shape[0] // (batch_size * sequence_length)
+
+ # Compute the mask that masks all padding tokens as 0 with the same shape of expert_mask
+ expert_attention_mask = (
+ attention_mask[None, :, :, None, None]
+ .expand((num_hidden_layers, batch_size, sequence_length, top_k, num_experts))
+ .reshape(-1, top_k, num_experts)
+ .to(compute_device)
+ )
+
+ # Compute the percentage of tokens routed to each experts
+ tokens_per_expert = torch.sum(expert_mask.float() * expert_attention_mask, dim=0) / torch.sum(
+ expert_attention_mask, dim=0
+ )
+
+ # Compute the mask that masks all padding tokens as 0 with the same shape of tokens_per_expert
+ router_per_expert_attention_mask = (
+ attention_mask[None, :, :, None]
+ .expand((num_hidden_layers, batch_size, sequence_length, num_experts))
+ .reshape(-1, num_experts)
+ .to(compute_device)
+ )
+
+ # Compute the average probability of routing to these experts
+ router_prob_per_expert = torch.sum(routing_weights * router_per_expert_attention_mask, dim=0) / torch.sum(
+ router_per_expert_attention_mask, dim=0
+ )
+
+ overall_loss = torch.sum(tokens_per_expert * router_prob_per_expert.unsqueeze(0))
+ return overall_loss * num_experts
+
+
+class Glm4vMoeForConditionalGeneration(Glm4vMoePreTrainedModel, GenerationMixin):
+ _tied_weights_keys = {"lm_head.weight": "model.language_model.embed_tokens.weight"}
+ # Reference: fix gemma3 grad acc #37208
+ accepts_loss_kwargs = False
+
+ def __init__(self, config):
+ super().__init__(config)
+ self.model = Glm4vMoeModel(config)
+ self.lm_head = nn.Linear(config.text_config.hidden_size, config.text_config.vocab_size, bias=False)
+ self.num_experts = config.text_config.num_local_experts
+ self.num_experts_per_tok = config.text_config.num_experts_per_tok
+
+ self.post_init()
+
+ def get_input_embeddings(self):
+ return self.model.get_input_embeddings()
+
+ def set_input_embeddings(self, value):
+ self.model.set_input_embeddings(value)
+
+ @auto_docstring
+ def get_video_features(
+ self,
+ pixel_values_videos: torch.FloatTensor,
+ video_grid_thw: torch.LongTensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | BaseModelOutputWithPooling:
+ r"""
+ pixel_values_videos (`torch.FloatTensor` of shape `(batch_size, num_channels, image_size, image_size)`):
+ The tensors corresponding to the input videos.
+ video_grid_thw (`torch.LongTensor` of shape `(num_videos, 3)`, *optional*):
+ The temporal, height and width of feature shape of each video in LLM.
+ """
+ return self.model.get_video_features(
+ pixel_values_videos=pixel_values_videos, video_grid_thw=video_grid_thw, **kwargs
+ )
+
+ @auto_docstring
+ def get_image_features(
+ self,
+ pixel_values: torch.FloatTensor,
+ image_grid_thw: torch.LongTensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | BaseModelOutputWithPooling:
+ r"""
+ pixel_values (`torch.FloatTensor` of shape `(batch_size, num_channels, image_size, image_size)`):
+ The tensors corresponding to the input images.
+ image_grid_thw (`torch.LongTensor` of shape `(num_images, 3)`, *optional*):
+ The temporal, height and width of feature shape of each image in LLM.
+ """
+ return self.model.get_image_features(pixel_values=pixel_values, image_grid_thw=image_grid_thw, **kwargs)
+
+ @auto_docstring
+ @can_return_tuple
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ pixel_values: torch.Tensor | None = None,
+ pixel_values_videos: torch.FloatTensor | None = None,
+ image_grid_thw: torch.LongTensor | None = None,
+ video_grid_thw: torch.LongTensor | None = None,
+ mm_token_type_ids: torch.IntTensor | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | Glm4vMoeCausalLMOutputWithPast:
+ r"""
+ 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]`.
+ 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.
+
+ Example:
+
+ ```python
+ >>> from PIL import Image
+ >>> import httpx
+ >>> from io import BytesIO
+ >>> from transformers import AutoProcessor, Glm4vMoeForConditionalGeneration
+
+ >>> model = Glm4vMoeForConditionalGeneration.from_pretrained("zai-org/GLM-4.1V-9B-Thinking")
+ >>> processor = AutoProcessor.from_pretrained("zai-org/GLM-4.1V-9B-Thinking")
+
+ >>> messages = [
+ {
+ "role": "user",
+ "content": [
+ {"type": "image", "url": "https://www.ilankelman.org/stopsigns/australia.jpg"},
+ {"type": "text", "text": "What is shown in this image?"},
+ ],
+ },
+ ]
+ >>> url = "https://www.ilankelman.org/stopsigns/australia.jpg"
+ >>> with httpx.stream("GET", url) as response:
+ ... image = Image.open(BytesIO(response.read()))
+
+ >>> text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
+ >>> inputs = processor(text=[text], images=[image], vision_infos=[vision_infos])
+
+ >>> # Generate
+ >>> generate_ids = model.generate(inputs.input_ids, max_length=30)
+ >>> 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 ..."
+ ```"""
+ outputs = self.model(
+ 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,
+ inputs_embeds=inputs_embeds,
+ mm_token_type_ids=mm_token_type_ids,
+ **kwargs,
+ )
+
+ hidden_states = outputs[0]
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(logits=logits, labels=labels, vocab_size=self.config.text_config.vocab_size)
+
+ aux_loss = None
+ if kwargs.get("output_router_logits", False):
+ aux_loss = load_balancing_loss_func(
+ outputs.router_logits,
+ self.num_experts,
+ self.num_experts_per_tok,
+ attention_mask,
+ )
+ if labels is not None:
+ loss += self.config.text_config.router_aux_loss_coef * aux_loss.to(
+ loss.device
+ ) # make sure to reside in the same device
+
+ return Glm4vMoeCausalLMOutputWithPast(
+ loss=loss,
+ aux_loss=aux_loss,
+ logits=logits,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ rope_deltas=outputs.rope_deltas,
+ router_logits=outputs.router_logits,
+ )
+
+ def prepare_inputs_for_generation(
+ self,
+ input_ids,
+ past_key_values=None,
+ attention_mask=None,
+ inputs_embeds=None,
+ position_ids=None,
+ use_cache=True,
+ pixel_values=None,
+ pixel_values_videos=None,
+ image_grid_thw=None,
+ video_grid_thw=None,
+ is_first_iteration=False,
+ **kwargs,
+ ):
+ # Overwritten -- in specific circumstances we don't want to forward image inputs to the model
+
+ model_inputs = super().prepare_inputs_for_generation(
+ input_ids,
+ past_key_values=past_key_values,
+ attention_mask=attention_mask,
+ inputs_embeds=inputs_embeds,
+ position_ids=position_ids,
+ pixel_values=pixel_values,
+ pixel_values_videos=pixel_values_videos,
+ image_grid_thw=image_grid_thw,
+ video_grid_thw=video_grid_thw,
+ use_cache=use_cache,
+ is_first_iteration=is_first_iteration,
+ **kwargs,
+ )
+
+ if not is_first_iteration and use_cache:
+ model_inputs["pixel_values"] = None
+ model_inputs["pixel_values_videos"] = None
+
+ return model_inputs
+
+ def _prepare_position_ids_for_generation(self, inputs_tensor, model_kwargs):
+ # Overwritten -- requires 3D position ids
+
+ text_positions = super()._prepare_position_ids_for_generation(inputs_tensor, model_kwargs)
+
+ # Early exit in case we are continuing generation from past kv
+ past_length = 0
+ if (cache := model_kwargs.get("past_key_values")) is not None:
+ past_length = cache.get_seq_length()
+ if past_length != 0 and self.model.rope_deltas is not None:
+ position_ids = text_positions[None, ...] + self.model.rope_deltas
+ return position_ids
+
+ # Otherwise compute 3d position ids for vision tokens and concat with text position ids
+ if "input_ids" in model_kwargs and model_kwargs["input_ids"].shape[1] > 0:
+ inputs_tensor = model_kwargs["input_ids"]
+
+ is_input_ids = len(inputs_tensor.shape) == 2 and inputs_tensor.dtype in [torch.int, torch.long]
+ if (
+ is_input_ids
+ and model_kwargs.get("mm_token_type_ids") is not None
+ and (model_kwargs.get("image_grid_thw") is not None or model_kwargs.get("video_grid_thw") is not None)
+ ):
+ model_kwargs = {k: v for k, v in model_kwargs.items() if k != "input_ids"}
+ vision_positions, rope_deltas = self.model.get_rope_index(inputs_tensor, **model_kwargs)
+ self.model.rope_deltas = rope_deltas
+ else:
+ vision_positions = text_positions.unsqueeze(0).expand(3, -1, -1)
+ self.model.rope_deltas = torch.zeros(
+ inputs_tensor.shape[0], 1, dtype=torch.long, device=inputs_tensor.device
+ )
+
+ # Concatenate "text + vision" positions into [4, bs, seq-len]
+ text_positions = text_positions[None, ...]
+ position_ids = torch.cat([text_positions, vision_positions], dim=0)
+
+ return position_ids
+
+ def _get_image_nums_and_video_nums(
+ self,
+ input_ids: torch.LongTensor | None,
+ inputs_embeds: torch.Tensor | None = None,
+ ) -> tuple[torch.Tensor, torch.Tensor]:
+ """
+ Get the number of images and videos for each sample to calculate the separation length of the sample tensor.
+ These parameters are not passed through the processor to avoid unpredictable impacts from interface modifications.
+
+ Args:
+ input_ids (`torch.LongTensor` of shape `(batch_size, sequence_length)`):
+ Indices of input sequence tokens in the vocabulary.
+
+ Returns:
+ image_nums (`torch.LongTensor` of shape `(batch_size, num_images_sample)`)
+ video_nums (`torch.LongTensor` of shape `(batch_size, num_videos_sample)`)
+ """
+
+ if inputs_embeds is not None:
+ is_image = (
+ inputs_embeds
+ == self.get_input_embeddings()(
+ torch.tensor(self.config.image_start_token_id, dtype=torch.long, device=inputs_embeds.device)
+ )
+ )[..., 0]
+ is_video_start = (
+ inputs_embeds
+ == self.get_input_embeddings()(
+ torch.tensor(self.config.video_start_token_id, dtype=torch.long, device=inputs_embeds.device)
+ )
+ )[..., 0]
+ is_video_end = (
+ inputs_embeds
+ == self.get_input_embeddings()(
+ torch.tensor(self.config.video_end_token_id, dtype=torch.long, device=inputs_embeds.device)
+ )
+ )[..., 0]
+ else:
+ is_image = input_ids == self.config.image_start_token_id
+ is_video_start = input_ids == self.config.video_start_token_id
+ is_video_end = input_ids == self.config.video_end_token_id
+
+ # Cumulative sum to track if we're inside a video span
+ # We'll assume well-formed video tags (i.e. matching starts and ends)
+ video_level = torch.cumsum(is_video_start.int() - is_video_end.int(), dim=1)
+ inside_video = video_level > 0 # shape (batch_size, seq_length)
+
+ # Mask out image tokens that are inside video spans
+ standalone_images = is_image & (~inside_video)
+
+ # Count per batch
+ image_counts = standalone_images.sum(dim=1)
+ video_counts = is_video_start.sum(dim=1)
+
+ return image_counts, video_counts
+
+ def _expand_inputs_for_generation(
+ self,
+ expand_size: int = 1,
+ is_encoder_decoder: bool = False,
+ input_ids: torch.LongTensor | None = None,
+ **model_kwargs,
+ ) -> tuple[torch.LongTensor, dict[str, Any]]:
+ # Overwritten -- Support for expanding tensors without a batch size dimension
+ # e.g., pixel_values, image_grid_thw, pixel_values_videos, video_grid_thw, second_per_grid_t
+ # pixel_values.shape[0] is sum(seqlen_images for samples)
+ # image_grid_thw.shape[0] is sum(num_images for samples)
+
+ if expand_size == 1:
+ return input_ids, model_kwargs
+
+ visual_keys = ["pixel_values", "image_grid_thw", "pixel_values_videos", "video_grid_thw", "second_per_grid_ts"]
+
+ def _expand_dict_for_generation_visual(dict_to_expand):
+ image_grid_thw = model_kwargs.get("image_grid_thw", None)
+ video_grid_thw = model_kwargs.get("video_grid_thw", None)
+ image_nums, video_nums = self._get_image_nums_and_video_nums(
+ input_ids, inputs_embeds=model_kwargs.get("inputs_embeds", None)
+ )
+
+ def _repeat_interleave_samples(x, lengths, repeat_times):
+ samples = torch.split(x, lengths)
+ repeat_args = [repeat_times] + [1] * (x.dim() - 1)
+ result = torch.cat([sample.repeat(*repeat_args) for sample in samples], dim=0)
+ return result
+
+ for key in dict_to_expand:
+ if key == "pixel_values":
+ # split images into samples
+ samples = torch.split(image_grid_thw, list(image_nums))
+ # compute the sequence length of images for each sample
+ lengths = [torch.prod(sample, dim=1).sum() for sample in samples]
+ dict_to_expand[key] = _repeat_interleave_samples(
+ dict_to_expand[key], lengths=lengths, repeat_times=expand_size
+ )
+ elif key == "image_grid_thw":
+ # get the num of images for each sample
+ lengths = list(image_nums)
+ dict_to_expand[key] = _repeat_interleave_samples(
+ dict_to_expand[key], lengths=lengths, repeat_times=expand_size
+ )
+ elif key == "pixel_values_videos":
+ samples = torch.split(video_grid_thw, list(video_nums))
+ lengths = [torch.prod(sample, dim=1).sum() for sample in samples]
+ dict_to_expand[key] = _repeat_interleave_samples(
+ dict_to_expand[key], lengths=lengths, repeat_times=expand_size
+ )
+ elif key == "video_grid_thw":
+ lengths = list(video_nums)
+ dict_to_expand[key] = _repeat_interleave_samples(
+ dict_to_expand[key], lengths=lengths, repeat_times=expand_size
+ )
+ elif key == "second_per_grid_ts":
+ dict_to_expand[key] = _repeat_interleave_samples(
+ dict_to_expand[key], lengths=list(video_nums), repeat_times=expand_size
+ )
+ return dict_to_expand
+
+ def _expand_dict_for_generation(dict_to_expand):
+ for key in dict_to_expand:
+ if key == "position_ids" and dict_to_expand[key].ndim == 3:
+ dict_to_expand[key] = dict_to_expand[key].repeat_interleave(expand_size, dim=1)
+ elif (
+ dict_to_expand[key] is not None
+ and isinstance(dict_to_expand[key], torch.Tensor)
+ and key not in visual_keys
+ ):
+ dict_to_expand[key] = dict_to_expand[key].repeat_interleave(expand_size, dim=0)
+ return dict_to_expand
+
+ model_kwargs = _expand_dict_for_generation_visual(model_kwargs)
+
+ if input_ids is not None:
+ input_ids = input_ids.repeat_interleave(expand_size, dim=0)
+
+ model_kwargs = _expand_dict_for_generation(model_kwargs)
+
+ if is_encoder_decoder:
+ if model_kwargs.get("encoder_outputs") is None:
+ raise ValueError("If `is_encoder_decoder` is True, make sure that `encoder_outputs` is defined.")
+ model_kwargs["encoder_outputs"] = _expand_dict_for_generation(model_kwargs["encoder_outputs"])
+
+ return input_ids, model_kwargs
+
+
+__all__ = [
+ "Glm4vMoeForConditionalGeneration",
+ "Glm4vMoeModel",
+ "Glm4vMoePreTrainedModel",
+ "Glm4vMoeTextModel",
+ "Glm4vMoeVisionModel",
+]
diff --git a/third_party/transformers/src/transformers/models/glm4v_moe/modular_glm4v_moe.py b/third_party/transformers/src/transformers/models/glm4v_moe/modular_glm4v_moe.py
new file mode 100644
index 0000000000000000000000000000000000000000..0929f3797e22b026b3114c62b6b0f4a7e95000ea
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/glm4v_moe/modular_glm4v_moe.py
@@ -0,0 +1,422 @@
+# Copyright 2025 The ZhipuAI Inc. team and HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from collections.abc import Callable
+
+import torch
+import torch.nn as nn
+from huggingface_hub.dataclasses import strict
+
+from ... import initialization as init
+from ...cache_utils import Cache, DynamicCache
+from ...masking_utils import create_causal_mask
+from ...modeling_flash_attention_utils import FlashAttentionKwargs
+from ...modeling_outputs import MoeModelOutputWithPast
+from ...modeling_utils import ALL_ATTENTION_FUNCTIONS
+from ...processing_utils import Unpack
+from ...utils import TransformersKwargs, auto_docstring, logging
+from ...utils.generic import can_return_tuple
+from ..deepseek_v3.modeling_deepseek_v3 import DeepseekV3NaiveMoe
+from ..glm4.modeling_glm4 import Glm4Attention
+from ..glm4_moe.configuration_glm4_moe import Glm4MoeConfig
+from ..glm4_moe.modeling_glm4_moe import (
+ Glm4MoeDecoderLayer,
+ Glm4MoeMLP,
+ Glm4MoeMoE,
+ Glm4MoePreTrainedModel,
+ Glm4MoeTopkRouter,
+ eager_attention_forward,
+)
+from ..glm4v.configuration_glm4v import Glm4vConfig
+from ..glm4v.modeling_glm4v import (
+ Glm4vForConditionalGeneration,
+ Glm4vTextModel,
+ Glm4vVisionModel,
+ Glm4vVisionRotaryEmbedding,
+)
+from ..gpt_neox.modeling_gpt_neox import apply_rotary_pos_emb
+from ..qwen3_vl_moe.modeling_qwen3_vl_moe import (
+ Qwen3VLMoeCausalLMOutputWithPast,
+ Qwen3VLMoeModelOutputWithPast,
+ load_balancing_loss_func,
+)
+
+
+logger = logging.get_logger(__name__)
+
+
+@auto_docstring(checkpoint="zai-org/GLM-4.5V")
+@strict
+class Glm4vMoeTextConfig(Glm4MoeConfig):
+ r"""
+ n_group (`int`, *optional*, defaults to 1):
+ Number of groups for routed experts.
+ first_k_dense_replace (`int`, *optional*, defaults to 1):
+ Number of dense layers in shallow layers(embed->dense->dense->...->dense->moe->moe...->lm_head).
+ \--k dense layers--/
+
+ Example:
+
+ ```python
+ >>> from transformers import Glm4vMoeTextModel, Glm4vMoeConfig
+
+ >>> # Initializing a GLM-4.5V style configuration
+ >>> configuration = Glm4vMoeConfig()
+
+ >>> # Initializing a model from the GLM-4.5V style configuration
+ >>> model = Glm4vMoeTextModel(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "glm4v_moe_text"
+ base_config_key = "text_config"
+ keys_to_ignore_at_inference = ["past_key_values"]
+ # Default tensor parallel plan for base model `Glm4vMoe`
+ base_model_tp_plan = {
+ "layers.*.self_attn.q_proj": "colwise",
+ "layers.*.self_attn.k_proj": "colwise",
+ "layers.*.self_attn.v_proj": "colwise",
+ "layers.*.self_attn.o_proj": "rowwise",
+ "layers.*.mlp.gate_proj": "colwise",
+ "layers.*.mlp.up_proj": "colwise",
+ "layers.*.mlp.down_proj": "rowwise",
+ }
+ base_model_pp_plan = {
+ "embed_tokens": (["input_ids"], ["inputs_embeds"]),
+ "layers": (["hidden_states", "attention_mask"], ["hidden_states"]),
+ "norm": (["hidden_states"], ["hidden_states"]),
+ }
+ ignore_keys_at_rope_validation = {"mrope_section"}
+
+ vocab_size: int = 151424
+ max_position_embeddings: int = 65536
+ attention_bias: bool = True
+ router_aux_loss_coef: float = 0.0001
+ use_qk_norm = AttributeError()
+
+ def __post_init__(self, **kwargs):
+ super().__post_init__(self, **kwargs)
+
+
+@auto_docstring(checkpoint="zai-org/GLM-4.5V")
+@strict
+class Glm4vMoeConfig(Glm4vConfig):
+ r"""
+ image_start_token_id (`int`, *optional*, defaults to 151339):
+ The image start token index to encode the start of image.
+ image_end_token_id (`int`, *optional*, defaults to 151340):
+ The image end token index to encode the end of image.
+ video_start_token_id (`int`, *optional*, defaults to 151341):
+ The video start token index to encode the start of video.
+ video_end_token_id (`int`, *optional*, defaults to 151342):
+ The video end token index to encode the end of video.
+
+ ```python
+ >>> from transformers import Glm4vMoeForConditionalGeneration, Glm4vMoeConfig
+
+ >>> # Initializing a GLM-4.5V style configuration
+ >>> configuration = Glm4vMoeConfig()
+
+ >>> # Initializing a model from the GLM-4.5V style configuration
+ >>> model = Glm4vMoeForConditionalGeneration(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ image_token_id: int = 151363
+ video_token_id: int = 151364
+
+
+class Glm4vMoeTextAttention(Glm4Attention):
+ def __init__(self, config: Glm4vMoeTextConfig, layer_idx: int | None = None):
+ super().__init__(config, layer_idx)
+ self.rope_parameters = config.rope_parameters
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor],
+ attention_mask: torch.Tensor | None,
+ past_key_values: Cache | None = None,
+ **kwargs: Unpack[FlashAttentionKwargs],
+ ) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
+ input_shape = hidden_states.shape[:-1]
+ hidden_shape = (*input_shape, -1, self.head_dim)
+
+ query_states = self.q_proj(hidden_states).view(hidden_shape)
+ key_states = self.k_proj(hidden_states).view(hidden_shape)
+ value_states = self.v_proj(hidden_states).view(hidden_shape)
+
+ query_states = query_states.transpose(1, 2)
+ key_states = key_states.transpose(1, 2)
+ value_states = value_states.transpose(1, 2)
+
+ cos, sin = position_embeddings
+ query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin)
+
+ if past_key_values is not None:
+ key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx)
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ query_states,
+ key_states,
+ value_states,
+ attention_mask,
+ dropout=0.0 if not self.training else self.attention_dropout,
+ scaling=self.scaling,
+ **kwargs,
+ )
+
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
+ attn_output = self.o_proj(attn_output)
+ return attn_output, attn_weights
+
+
+class Glm4vMoeTextTopkRouter(Glm4MoeTopkRouter, nn.Module):
+ def __init__(self, config: Glm4vMoeTextConfig):
+ super().__init__(config)
+
+
+class Glm4vMoeTextNaiveMoe(DeepseekV3NaiveMoe):
+ pass
+
+
+class Glm4vMoeTextMoE(Glm4MoeMoE):
+ def __init__(self, config: Glm4vMoeTextConfig):
+ super().__init__(config)
+ self.config = config
+ self.experts = Glm4vMoeTextNaiveMoe(config)
+ self.gate = Glm4vMoeTextTopkRouter(config)
+ self.shared_experts = Glm4vMoeTextMLP(
+ config=config, intermediate_size=config.moe_intermediate_size * config.n_shared_experts
+ )
+
+
+class Glm4vMoeTextMLP(Glm4MoeMLP):
+ pass
+
+
+class Glm4vMoeTextDecoderLayer(Glm4MoeDecoderLayer):
+ def __init__(self, config: Glm4vMoeTextConfig, layer_idx: int):
+ super().__init__(config, layer_idx)
+
+
+class Glm4vMoePreTrainedModel(Glm4MoePreTrainedModel):
+ config: Glm4vMoeConfig
+ base_model_prefix = "model"
+ input_modalities = ("text", "image", "video")
+ _no_split_modules = ["Glm4vMoeTextDecoderLayer", "Glm4vMoeVisionBlock"]
+ _skip_keys_device_placement = "past_key_values"
+ _can_record_outputs = {}
+
+ def _init_weights(self, module):
+ super()._init_weights(module)
+ if isinstance(module, Glm4vMoeVisionRotaryEmbedding):
+ inv_freq = 1.0 / (module.theta ** (torch.arange(0, module.dim, 2, dtype=torch.float) / module.dim))
+ init.copy_(module.inv_freq, inv_freq)
+
+
+class Glm4vMoeCausalLMOutputWithPast(Qwen3VLMoeCausalLMOutputWithPast):
+ pass
+
+
+class Glm4vMoeVisionRotaryEmbedding(Glm4vVisionRotaryEmbedding):
+ pass
+
+
+@auto_docstring
+class Glm4vMoeVisionModel(Glm4vVisionModel):
+ pass
+
+
+@auto_docstring
+class Glm4vMoeTextModel(Glm4vTextModel):
+ _can_record_outputs = {
+ "hidden_states": Glm4vMoeTextDecoderLayer,
+ "attentions": Glm4vMoeTextAttention,
+ "router_logits": Glm4vMoeTextTopkRouter,
+ }
+
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ use_cache: bool | None = None,
+ **kwargs: Unpack[FlashAttentionKwargs],
+ ) -> tuple | MoeModelOutputWithPast:
+ if (input_ids is None) ^ (inputs_embeds is not None):
+ raise ValueError("You must specify exactly one of input_ids or inputs_embeds")
+
+ # torch.jit.trace() doesn't support cache objects in the output
+ if use_cache and past_key_values is None and not torch.jit.is_tracing():
+ past_key_values = DynamicCache(config=self.config)
+
+ if inputs_embeds is None:
+ inputs_embeds = self.embed_tokens(input_ids)
+
+ # the hard coded `3` is for temporal, height and width.
+ if position_ids is None:
+ past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
+ position_ids = torch.arange(inputs_embeds.shape[1], device=inputs_embeds.device) + past_seen_tokens
+ position_ids = position_ids.view(1, 1, -1).expand(3, inputs_embeds.shape[0], -1)
+ elif position_ids.ndim == 2:
+ position_ids = position_ids[None, ...].expand(3, position_ids.shape[0], -1)
+
+ # NOTE: we need to pass text position ids for packing. Qwen2-VL uses 3D positions
+ # where each dim indicates visual spatial positions for temporal/height/width grids.
+ # There are two scenarios when FA2-like packed masking might be activated.
+ # 1. User specifically passed packed `position_ids` and no attention mask.
+ # In this case we expect the useer to create correct position ids for all 3 grids
+ # and prepend text-only position ids to it. The final tensor will be [4, bs, seq-len]
+ # 2. User runs forward with no attention mask and no position ids. In this case, position ids
+ # are prepared by the model (`get_rope_index`) as `[4, bs, seq-len]` tensor. Text-only positions are
+ # prepended by us when creating positions so that the mask is constructed correctly. NOTE: failing to pass
+ # text-only positions will cause incorrect mask construction, do not change `prepare_input_for_generation`
+ if position_ids.ndim == 3 and position_ids.shape[0] == 4:
+ text_position_ids = position_ids[0]
+ position_ids = position_ids[1:]
+ else:
+ # If inputs are not packed (usual 3D positions), do not prepare mask from position_ids
+ text_position_ids = None
+
+ mask_kwargs = {
+ "config": self.config,
+ "inputs_embeds": inputs_embeds,
+ "attention_mask": attention_mask,
+ "past_key_values": past_key_values,
+ "position_ids": text_position_ids,
+ }
+ # Create the masks
+ causal_mask = create_causal_mask(**mask_kwargs)
+
+ hidden_states = inputs_embeds
+
+ # create position embeddings to be shared across the decoder layers
+ position_embeddings = self.rotary_emb(hidden_states, position_ids)
+
+ for i, decoder_layer in enumerate(self.layers[: self.config.num_hidden_layers]):
+ layer_outputs = decoder_layer(
+ hidden_states,
+ position_embeddings=position_embeddings,
+ attention_mask=causal_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ **kwargs,
+ )
+ hidden_states = layer_outputs
+
+ hidden_states = self.norm(hidden_states)
+
+ return MoeModelOutputWithPast(
+ last_hidden_state=hidden_states,
+ past_key_values=past_key_values,
+ )
+
+
+class Glm4vMoeModelOutputWithPast(Qwen3VLMoeModelOutputWithPast):
+ pass
+
+
+class Glm4vMoeForConditionalGeneration(Glm4vForConditionalGeneration):
+ def __init__(self, config):
+ super().__init__(config)
+ self.num_experts = config.text_config.num_local_experts
+ self.num_experts_per_tok = config.text_config.num_experts_per_tok
+
+ @auto_docstring
+ @can_return_tuple
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ pixel_values: torch.Tensor | None = None,
+ pixel_values_videos: torch.FloatTensor | None = None,
+ image_grid_thw: torch.LongTensor | None = None,
+ video_grid_thw: torch.LongTensor | None = None,
+ mm_token_type_ids: torch.IntTensor | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | Glm4vMoeCausalLMOutputWithPast:
+ outputs = self.model(
+ 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,
+ inputs_embeds=inputs_embeds,
+ mm_token_type_ids=mm_token_type_ids,
+ **kwargs,
+ )
+
+ hidden_states = outputs[0]
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(logits=logits, labels=labels, vocab_size=self.config.text_config.vocab_size)
+
+ aux_loss = None
+ if kwargs.get("output_router_logits", False):
+ aux_loss = load_balancing_loss_func(
+ outputs.router_logits,
+ self.num_experts,
+ self.num_experts_per_tok,
+ attention_mask,
+ )
+ if labels is not None:
+ loss += self.config.text_config.router_aux_loss_coef * aux_loss.to(
+ loss.device
+ ) # make sure to reside in the same device
+
+ return Glm4vMoeCausalLMOutputWithPast(
+ loss=loss,
+ aux_loss=aux_loss,
+ logits=logits,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ rope_deltas=outputs.rope_deltas,
+ router_logits=outputs.router_logits,
+ )
+
+
+__all__ = [
+ "Glm4vMoeConfig",
+ "Glm4vMoeVisionConfig", # noqa: F822
+ "Glm4vMoeTextConfig",
+ "Glm4vMoeForConditionalGeneration",
+ "Glm4vMoeModel", # noqa: F822
+ "Glm4vMoePreTrainedModel",
+ "Glm4vMoeTextModel",
+ "Glm4vMoeVisionModel",
+]
diff --git a/third_party/transformers/src/transformers/models/glpn/__init__.py b/third_party/transformers/src/transformers/models/glpn/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..1f8ab92c299a2e6253dbf0d463fbcfba6853d619
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/glpn/__init__.py
@@ -0,0 +1,29 @@
+# Copyright 2024 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_glpn import *
+ from .image_processing_glpn import *
+ from .image_processing_pil_glpn import *
+ from .modeling_glpn import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/glpn/configuration_glpn.py b/third_party/transformers/src/transformers/models/glpn/configuration_glpn.py
new file mode 100644
index 0000000000000000000000000000000000000000..feed10de85f2c2f3ff703f93fce350f2f9d758d3
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/glpn/configuration_glpn.py
@@ -0,0 +1,85 @@
+# Copyright 2022 KAIST and The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""GLPN model configuration"""
+
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...utils import auto_docstring
+
+
+@auto_docstring(checkpoint="vinvino02/glpn-kitti")
+@strict
+class GLPNConfig(PreTrainedConfig):
+ r"""
+ num_encoder_blocks (`int`, *optional*, defaults to 4):
+ The number of encoder blocks (i.e. stages in the Mix Transformer encoder).
+ depths (`list[int]`, *optional*, defaults to `[2, 2, 2, 2]`):
+ The number of layers in each encoder block.
+ sr_ratios (`list[int]`, *optional*, defaults to `[8, 4, 2, 1]`):
+ Sequence reduction ratios in each encoder block.
+ patch_sizes (`list[int]`, *optional*, defaults to `[7, 3, 3, 3]`):
+ Patch size before each encoder block.
+ strides (`list[int]`, *optional*, defaults to `[4, 2, 2, 2]`):
+ Stride before each encoder block.
+ num_attention_heads (`list[int]`, *optional*, defaults to `[1, 2, 5, 8]`):
+ Number of attention heads for each attention layer in each block of the Transformer encoder.
+ mlp_ratios (`list[int]`, *optional*, defaults to `[4, 4, 4, 4]`):
+ Ratio of the size of the hidden layer compared to the size of the input layer of the Mix FFNs in the
+ encoder blocks.
+ decoder_hidden_size (`int`, *optional*, defaults to 64):
+ The dimension of the decoder.
+ max_depth (`int`, *optional*, defaults to 10):
+ The maximum depth of the decoder.
+ head_in_index (`int`, *optional*, defaults to -1):
+ The index of the features to use in the head.
+
+ Example:
+
+ ```python
+ >>> from transformers import GLPNModel, GLPNConfig
+
+ >>> # Initializing a GLPN vinvino02/glpn-kitti style configuration
+ >>> configuration = GLPNConfig()
+
+ >>> # Initializing a model from the vinvino02/glpn-kitti style configuration
+ >>> model = GLPNModel(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "glpn"
+
+ num_channels: int = 3
+ num_encoder_blocks: int = 4
+ depths: list[int] | tuple[int, ...] = (2, 2, 2, 2)
+ sr_ratios: list[int] | tuple[int, ...] = (8, 4, 2, 1)
+ hidden_sizes: list[int] | tuple[int, ...] = (32, 64, 160, 256)
+ patch_sizes: list[int] | tuple[int, ...] = (7, 3, 3, 3)
+ strides: list[int] | tuple[int, ...] = (4, 2, 2, 2)
+ num_attention_heads: list[int] | tuple[int, ...] = (1, 2, 5, 8)
+ mlp_ratios: list[int] | tuple[int, ...] = (4, 4, 4, 4)
+ hidden_act: str = "gelu"
+ hidden_dropout_prob: float | int = 0.0
+ attention_probs_dropout_prob: float | int = 0.0
+ initializer_range: float = 0.02
+ drop_path_rate: float | int = 0.1
+ layer_norm_eps: float = 1e-6
+ decoder_hidden_size: int = 64
+ max_depth: int = 10
+ head_in_index: int = -1
+
+
+__all__ = ["GLPNConfig"]
diff --git a/third_party/transformers/src/transformers/models/glpn/convert_glpn_to_pytorch.py b/third_party/transformers/src/transformers/models/glpn/convert_glpn_to_pytorch.py
new file mode 100644
index 0000000000000000000000000000000000000000..1473951c5263a0ecebbc52d2e3918dc6cd3e45d2
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/glpn/convert_glpn_to_pytorch.py
@@ -0,0 +1,208 @@
+# Copyright 2022 The HuggingFace Inc. team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Convert GLPN checkpoints."""
+
+import argparse
+from collections import OrderedDict
+from io import BytesIO
+
+import httpx
+import torch
+from PIL import Image
+
+from transformers import GLPNConfig, GLPNForDepthEstimation, GLPNImageProcessor
+from transformers.utils import logging
+
+
+logging.set_verbosity_info()
+logger = logging.get_logger(__name__)
+
+
+def rename_keys(state_dict):
+ new_state_dict = OrderedDict()
+ for key, value in state_dict.items():
+ if key.startswith("module.encoder"):
+ key = key.replace("module.encoder", "glpn.encoder")
+ if key.startswith("module.decoder"):
+ key = key.replace("module.decoder", "decoder.stages")
+ if "patch_embed" in key:
+ # replace for example patch_embed1 by patch_embeddings.0
+ idx = key[key.find("patch_embed") + len("patch_embed")]
+ key = key.replace(f"patch_embed{idx}", f"patch_embeddings.{int(idx) - 1}")
+ if "norm" in key:
+ key = key.replace("norm", "layer_norm")
+ if "glpn.encoder.layer_norm" in key:
+ # replace for example layer_norm1 by layer_norm.0
+ idx = key[key.find("glpn.encoder.layer_norm") + len("glpn.encoder.layer_norm")]
+ key = key.replace(f"layer_norm{idx}", f"layer_norm.{int(idx) - 1}")
+ if "layer_norm1" in key:
+ key = key.replace("layer_norm1", "layer_norm_1")
+ if "layer_norm2" in key:
+ key = key.replace("layer_norm2", "layer_norm_2")
+ if "block" in key:
+ # replace for example block1 by block.0
+ idx = key[key.find("block") + len("block")]
+ key = key.replace(f"block{idx}", f"block.{int(idx) - 1}")
+ if "attn.q" in key:
+ key = key.replace("attn.q", "attention.self.query")
+ if "attn.proj" in key:
+ key = key.replace("attn.proj", "attention.output.dense")
+ if "attn" in key:
+ key = key.replace("attn", "attention.self")
+ if "fc1" in key:
+ key = key.replace("fc1", "dense1")
+ if "fc2" in key:
+ key = key.replace("fc2", "dense2")
+ if "linear_pred" in key:
+ key = key.replace("linear_pred", "classifier")
+ if "linear_fuse" in key:
+ key = key.replace("linear_fuse.conv", "linear_fuse")
+ key = key.replace("linear_fuse.bn", "batch_norm")
+ if "linear_c" in key:
+ # replace for example linear_c4 by linear_c.3
+ idx = key[key.find("linear_c") + len("linear_c")]
+ key = key.replace(f"linear_c{idx}", f"linear_c.{int(idx) - 1}")
+ if "bot_conv" in key:
+ key = key.replace("bot_conv", "0.convolution")
+ if "skip_conv1" in key:
+ key = key.replace("skip_conv1", "1.convolution")
+ if "skip_conv2" in key:
+ key = key.replace("skip_conv2", "2.convolution")
+ if "fusion1" in key:
+ key = key.replace("fusion1", "1.fusion")
+ if "fusion2" in key:
+ key = key.replace("fusion2", "2.fusion")
+ if "fusion3" in key:
+ key = key.replace("fusion3", "3.fusion")
+ if "fusion" in key and "conv" in key:
+ key = key.replace("conv", "convolutional_layer")
+ if key.startswith("module.last_layer_depth"):
+ key = key.replace("module.last_layer_depth", "head.head")
+ new_state_dict[key] = value
+
+ return new_state_dict
+
+
+def read_in_k_v(state_dict, config):
+ # for each of the encoder blocks:
+ for i in range(config.num_encoder_blocks):
+ for j in range(config.depths[i]):
+ # read in weights + bias of keys and values (which is a single matrix in the original implementation)
+ kv_weight = state_dict.pop(f"glpn.encoder.block.{i}.{j}.attention.self.kv.weight")
+ kv_bias = state_dict.pop(f"glpn.encoder.block.{i}.{j}.attention.self.kv.bias")
+ # next, add keys and values (in that order) to the state dict
+ state_dict[f"glpn.encoder.block.{i}.{j}.attention.self.key.weight"] = kv_weight[
+ : config.hidden_sizes[i], :
+ ]
+ state_dict[f"glpn.encoder.block.{i}.{j}.attention.self.key.bias"] = kv_bias[: config.hidden_sizes[i]]
+ state_dict[f"glpn.encoder.block.{i}.{j}.attention.self.value.weight"] = kv_weight[
+ config.hidden_sizes[i] :, :
+ ]
+ state_dict[f"glpn.encoder.block.{i}.{j}.attention.self.value.bias"] = kv_bias[config.hidden_sizes[i] :]
+
+
+# We will verify our results on a COCO image
+def prepare_img():
+ url = "http://images.cocodataset.org/val2017/000000039769.jpg"
+ with httpx.stream("GET", url) as response:
+ image = Image.open(BytesIO(response.read()))
+
+ return image
+
+
+@torch.no_grad()
+def convert_glpn_checkpoint(checkpoint_path, pytorch_dump_folder_path, push_to_hub=False, model_name=None):
+ """
+ Copy/paste/tweak model's weights to our GLPN structure.
+ """
+
+ # load GLPN configuration (Segformer-B4 size)
+ config = GLPNConfig(hidden_sizes=[64, 128, 320, 512], decoder_hidden_size=64, depths=[3, 8, 27, 3])
+
+ # load image processor (only resize + rescale)
+ image_processor = GLPNImageProcessor()
+
+ # prepare image
+ image = prepare_img()
+ pixel_values = image_processor(images=image, return_tensors="pt").pixel_values
+
+ logger.info("Converting model...")
+
+ # load original state dict
+ state_dict = torch.load(checkpoint_path, map_location=torch.device("cpu"), weights_only=True)
+
+ # rename keys
+ state_dict = rename_keys(state_dict)
+
+ # key and value matrices need special treatment
+ read_in_k_v(state_dict, config)
+
+ # create HuggingFace model and load state dict
+ model = GLPNForDepthEstimation(config)
+ model.load_state_dict(state_dict)
+ model.eval()
+
+ # forward pass
+ outputs = model(pixel_values)
+ predicted_depth = outputs.predicted_depth
+
+ # verify output
+ if model_name is not None:
+ if "nyu" in model_name:
+ expected_slice = torch.tensor(
+ [[4.4147, 4.0873, 4.0673], [3.7890, 3.2881, 3.1525], [3.7674, 3.5423, 3.4913]]
+ )
+ elif "kitti" in model_name:
+ expected_slice = torch.tensor(
+ [[3.4291, 2.7865, 2.5151], [3.2841, 2.7021, 2.3502], [3.1147, 2.4625, 2.2481]]
+ )
+ else:
+ raise ValueError(f"Unknown model name: {model_name}")
+
+ expected_shape = torch.Size([1, 480, 640])
+
+ assert predicted_depth.shape == expected_shape
+ assert torch.allclose(predicted_depth[0, :3, :3], expected_slice, atol=1e-4)
+ print("Looks ok!")
+
+ # finally, push to hub if required
+ if push_to_hub:
+ logger.info("Pushing model and image processor to the hub...")
+ model.push_to_hub(repo_id=f"nielsr/{model_name}")
+ image_processor.push_to_hub(repo_id=f"nielsr/{model_name}")
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+
+ parser.add_argument(
+ "--checkpoint_path",
+ default=None,
+ type=str,
+ help="Path to the original PyTorch checkpoint (.pth file).",
+ )
+ parser.add_argument(
+ "--pytorch_dump_folder_path", default=None, type=str, help="Path to the folder to output PyTorch model."
+ )
+ parser.add_argument(
+ "--push_to_hub", action="store_true", help="Whether to upload the model to the HuggingFace hub."
+ )
+ parser.add_argument(
+ "--model_name",
+ default="glpn-kitti",
+ type=str,
+ help="Name of the model in case you're pushing to the hub.",
+ )
+ args = parser.parse_args()
+ convert_glpn_checkpoint(args.checkpoint_path, args.pytorch_dump_folder_path, args.push_to_hub, args.model_name)
diff --git a/third_party/transformers/src/transformers/models/glpn/image_processing_glpn.py b/third_party/transformers/src/transformers/models/glpn/image_processing_glpn.py
new file mode 100644
index 0000000000000000000000000000000000000000..3a83bdd964643f7938836c3de1e07bec03b62d1a
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/glpn/image_processing_glpn.py
@@ -0,0 +1,152 @@
+# Copyright 2022 The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Image processor class for GLPN."""
+
+from typing import TYPE_CHECKING
+
+from ...image_processing_backends import TorchvisionBackend
+from ...image_processing_utils import BatchFeature
+from ...image_transforms import group_images_by_shape, reorder_images
+from ...image_utils import (
+ ImageInput,
+ PILImageResampling,
+ SizeDict,
+)
+from ...processing_utils import ImagesKwargs, Unpack
+from ...utils import TensorType, auto_docstring, logging, requires_backends
+
+
+if TYPE_CHECKING:
+ from ...modeling_outputs import DepthEstimatorOutput
+
+import torch
+from torchvision.transforms.v2 import functional as tvF
+
+
+logger = logging.get_logger(__name__)
+
+
+class GLPNImageProcessorKwargs(ImagesKwargs, total=False):
+ """
+ size_divisor (`int`, *optional*, defaults to 32):
+ When `do_resize` is `True`, images are resized so their height and width are rounded down to the closest
+ multiple of `size_divisor`.
+ """
+
+ size_divisor: int
+
+
+@auto_docstring
+class GLPNImageProcessor(TorchvisionBackend):
+ """Torchvision backend for GLPN with size_divisor resize."""
+
+ valid_kwargs = GLPNImageProcessorKwargs
+
+ do_resize = True
+ do_rescale = True
+ rescale_factor = 1 / 255
+ resample = PILImageResampling.BILINEAR
+ size_divisor = 32
+
+ def __init__(self, **kwargs: Unpack[GLPNImageProcessorKwargs]):
+ super().__init__(**kwargs)
+
+ def _validate_preprocess_kwargs(self, **kwargs):
+ # pop `do_resize` to not raise an error as `size` is not used (we use size_divisor)
+ kwargs.pop("do_resize", None)
+ return super()._validate_preprocess_kwargs(**kwargs)
+
+ @auto_docstring
+ def preprocess(self, images: ImageInput, **kwargs: Unpack[GLPNImageProcessorKwargs]) -> BatchFeature:
+ return super().preprocess(images, **kwargs)
+
+ def resize(
+ self,
+ image: "torch.Tensor",
+ size: SizeDict,
+ resample: "PILImageResampling | tvF.InterpolationMode | int | None",
+ size_divisor: int = 32,
+ **kwargs,
+ ) -> "torch.Tensor":
+ """Resize so height and width are rounded down to the closest multiple of size_divisor."""
+ height, width = image.shape[-2:]
+ new_h = height // size_divisor * size_divisor
+ new_w = width // size_divisor * size_divisor
+ return super().resize(
+ image,
+ SizeDict(height=new_h, width=new_w),
+ resample=resample,
+ **kwargs,
+ )
+
+ def _preprocess(
+ self,
+ images: list["torch.Tensor"],
+ do_resize: bool,
+ size: SizeDict,
+ resample: "PILImageResampling | tvF.InterpolationMode | int | None",
+ do_center_crop: bool,
+ crop_size: SizeDict,
+ do_rescale: bool,
+ rescale_factor: float,
+ do_normalize: bool,
+ image_mean: float | list[float] | None,
+ image_std: float | list[float] | None,
+ do_pad: bool | None,
+ pad_size: SizeDict | None,
+ disable_grouping: bool | None,
+ return_tensors: str | TensorType | None,
+ size_divisor: int = 32,
+ **kwargs,
+ ) -> BatchFeature:
+ """Custom preprocessing for GLPN."""
+ grouped_images, grouped_images_index = group_images_by_shape(images, disable_grouping=disable_grouping)
+ processed_images_grouped = {}
+ for shape, stacked_images in grouped_images.items():
+ if do_resize:
+ stacked_images = self.resize(stacked_images, size, resample, size_divisor=size_divisor)
+ stacked_images = self.rescale_and_normalize(
+ stacked_images, do_rescale, rescale_factor, do_normalize, image_mean, image_std
+ )
+ processed_images_grouped[shape] = stacked_images
+ processed_images = reorder_images(processed_images_grouped, grouped_images_index)
+ return BatchFeature(data={"pixel_values": processed_images}, tensor_type=return_tensors)
+
+ def post_process_depth_estimation(
+ self,
+ outputs: "DepthEstimatorOutput",
+ target_sizes: TensorType | list[tuple[int, int]] | None = None,
+ ) -> list[dict[str, TensorType]]:
+ """
+ Convert raw model outputs to final depth predictions.
+ Only supports PyTorch.
+ """
+ requires_backends(self, "torch")
+ predicted_depth = outputs.predicted_depth
+ if target_sizes is not None and len(predicted_depth) != len(target_sizes):
+ raise ValueError(
+ "Make sure that you pass in as many target sizes as the batch dimension of the predicted depth"
+ )
+ results = []
+ target_sizes = [None] * len(predicted_depth) if target_sizes is None else target_sizes
+ for depth, target_size in zip(predicted_depth, target_sizes):
+ if target_size is not None:
+ depth = depth[None, None, ...]
+ depth = torch.nn.functional.interpolate(depth, size=target_size, mode="bicubic", align_corners=False)
+ depth = depth.squeeze(0).squeeze(0)
+ results.append({"predicted_depth": depth})
+ return results
+
+
+__all__ = ["GLPNImageProcessor"]
diff --git a/third_party/transformers/src/transformers/models/glpn/image_processing_pil_glpn.py b/third_party/transformers/src/transformers/models/glpn/image_processing_pil_glpn.py
new file mode 100644
index 0000000000000000000000000000000000000000..fbfcfd8569d7bbac77ae4dc512ca0f3798fec175
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/glpn/image_processing_pil_glpn.py
@@ -0,0 +1,140 @@
+# Copyright 2022 The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Image processor class for GLPN."""
+
+from typing import TYPE_CHECKING
+
+import numpy as np
+
+from ...image_processing_backends import PilBackend
+from ...image_processing_utils import BatchFeature
+from ...image_utils import ImageInput, PILImageResampling, SizeDict
+from ...processing_utils import ImagesKwargs, Unpack
+from ...utils import TensorType, auto_docstring, logging
+from ...utils.import_utils import requires
+
+
+if TYPE_CHECKING:
+ from ...modeling_outputs import DepthEstimatorOutput
+
+logger = logging.get_logger(__name__)
+
+
+# Adapted from transformers.models.glpn.image_processing_glpn.GLPNImageProcessorKwargs
+class GLPNImageProcessorKwargs(ImagesKwargs, total=False):
+ """
+ size_divisor (`int`, *optional*, defaults to 32):
+ When `do_resize` is `True`, images are resized so their height and width are rounded down to the closest
+ multiple of `size_divisor`.
+ """
+
+ size_divisor: int
+
+
+@auto_docstring
+class GLPNImageProcessorPil(PilBackend):
+ """PIL backend for GLPN with size_divisor resize."""
+
+ valid_kwargs = GLPNImageProcessorKwargs
+
+ do_resize = True
+ do_rescale = True
+ rescale_factor = 1 / 255
+ resample = PILImageResampling.BILINEAR
+ size_divisor = 32
+
+ def __init__(self, **kwargs: Unpack[GLPNImageProcessorKwargs]):
+ super().__init__(**kwargs)
+
+ def _validate_preprocess_kwargs(self, **kwargs):
+ # pop `do_resize` to not raise an error as `size` is not used (we use size_divisor)
+ kwargs.pop("do_resize", None)
+ return super()._validate_preprocess_kwargs(**kwargs)
+
+ @auto_docstring
+ def preprocess(self, images: ImageInput, **kwargs: Unpack[GLPNImageProcessorKwargs]) -> BatchFeature:
+ return super().preprocess(images, **kwargs)
+
+ def resize(
+ self,
+ image: np.ndarray,
+ size: SizeDict,
+ resample: PILImageResampling | None,
+ size_divisor: int = 32,
+ **kwargs,
+ ) -> np.ndarray:
+ """Resize so height and width are rounded down to the closest multiple of size_divisor."""
+ height, width = image.shape[-2:]
+ new_h = height // size_divisor * size_divisor
+ new_w = width // size_divisor * size_divisor
+ return super().resize(image, SizeDict(height=new_h, width=new_w), resample, **kwargs)
+
+ def _preprocess(
+ self,
+ images: list[np.ndarray],
+ do_resize: bool,
+ size: SizeDict,
+ resample: PILImageResampling | None,
+ do_center_crop: bool,
+ crop_size: SizeDict,
+ do_rescale: bool,
+ rescale_factor: float,
+ do_normalize: bool,
+ image_mean: float | list[float] | None,
+ image_std: float | list[float] | None,
+ do_pad: bool | None,
+ pad_size: SizeDict | None,
+ return_tensors: str | TensorType | None,
+ size_divisor: int = 32,
+ **kwargs,
+ ) -> BatchFeature:
+ """Custom preprocessing for GLPN."""
+ processed_images = []
+ for image in images:
+ if do_resize:
+ image = self.resize(image, size, resample, size_divisor=size_divisor)
+ if do_rescale:
+ image = self.rescale(image, rescale_factor)
+ if do_normalize:
+ image = self.normalize(image, image_mean, image_std)
+ processed_images.append(image)
+ return BatchFeature(data={"pixel_values": processed_images}, tensor_type=return_tensors)
+
+ @requires(backends=("torch",))
+ def post_process_depth_estimation(
+ self, outputs: "DepthEstimatorOutput", target_sizes: TensorType | list[tuple[int, int]] | None = None
+ ) -> list[dict[str, TensorType]]:
+ """
+ Convert raw model outputs to final depth predictions.
+ Only supports PyTorch.
+ """
+ import torch.nn.functional as F
+
+ predicted_depth = outputs.predicted_depth
+ if target_sizes is not None and len(predicted_depth) != len(target_sizes):
+ raise ValueError(
+ "Make sure that you pass in as many target sizes as the batch dimension of the predicted depth"
+ )
+ results = []
+ target_sizes = [None] * len(predicted_depth) if target_sizes is None else target_sizes
+ for depth, target_size in zip(predicted_depth, target_sizes):
+ if target_size is not None:
+ depth = depth[None, None, ...]
+ depth = F.interpolate(depth, size=target_size, mode="bicubic", align_corners=False)
+ depth = depth.squeeze(0).squeeze(0)
+ results.append({"predicted_depth": depth})
+ return results
+
+
+__all__ = ["GLPNImageProcessorPil"]
diff --git a/third_party/transformers/src/transformers/models/glpn/modeling_glpn.py b/third_party/transformers/src/transformers/models/glpn/modeling_glpn.py
new file mode 100644
index 0000000000000000000000000000000000000000..2150c98d7e4cad77297b43a043d9b87fcefa44f5
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/glpn/modeling_glpn.py
@@ -0,0 +1,677 @@
+# Copyright 2022 KAIST and The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""PyTorch GLPN model."""
+
+import math
+
+import torch
+from torch import nn
+
+from ...activations import ACT2FN
+from ...modeling_outputs import BaseModelOutput, DepthEstimatorOutput
+from ...modeling_utils import PreTrainedModel
+from ...utils import auto_docstring, logging
+from .configuration_glpn import GLPNConfig
+
+
+logger = logging.get_logger(__name__)
+
+
+# Copied from transformers.models.beit.modeling_beit.drop_path
+def drop_path(input: torch.Tensor, drop_prob: float = 0.0, training: bool = False) -> torch.Tensor:
+ """
+ Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).
+
+ """
+ if drop_prob == 0.0 or not training:
+ return input
+ keep_prob = 1 - drop_prob
+ shape = (input.shape[0],) + (1,) * (input.ndim - 1) # work with diff dim tensors, not just 2D ConvNets
+ random_tensor = keep_prob + torch.rand(shape, dtype=input.dtype, device=input.device)
+ random_tensor.floor_() # binarize
+ output = input.div(keep_prob) * random_tensor
+ return output
+
+
+# Copied from transformers.models.segformer.modeling_segformer.SegformerDropPath
+class GLPNDropPath(nn.Module):
+ """Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks)."""
+
+ def __init__(self, drop_prob: float | None = None) -> None:
+ super().__init__()
+ self.drop_prob = drop_prob
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ return drop_path(hidden_states, self.drop_prob, self.training)
+
+ def extra_repr(self) -> str:
+ return f"p={self.drop_prob}"
+
+
+# Copied from transformers.models.segformer.modeling_segformer.SegformerOverlapPatchEmbeddings
+class GLPNOverlapPatchEmbeddings(nn.Module):
+ """Construct the overlapping patch embeddings."""
+
+ def __init__(self, patch_size, stride, num_channels, hidden_size):
+ super().__init__()
+ self.proj = nn.Conv2d(
+ num_channels,
+ hidden_size,
+ kernel_size=patch_size,
+ stride=stride,
+ padding=patch_size // 2,
+ )
+
+ self.layer_norm = nn.LayerNorm(hidden_size)
+
+ def forward(self, pixel_values):
+ embeddings = self.proj(pixel_values)
+ _, _, height, width = embeddings.shape
+ # (batch_size, num_channels, height, width) -> (batch_size, num_channels, height*width) -> (batch_size, height*width, num_channels)
+ # this can be fed to a Transformer layer
+ embeddings = embeddings.flatten(2).transpose(1, 2)
+ embeddings = self.layer_norm(embeddings)
+ return embeddings, height, width
+
+
+# Copied from transformers.models.segformer.modeling_segformer.SegformerEfficientSelfAttention
+class GLPNEfficientSelfAttention(nn.Module):
+ """SegFormer's efficient self-attention mechanism. Employs the sequence reduction process introduced in the [PvT
+ paper](https://huggingface.co/papers/2102.12122)."""
+
+ def __init__(self, config, hidden_size, num_attention_heads, sequence_reduction_ratio):
+ super().__init__()
+ self.hidden_size = hidden_size
+ self.num_attention_heads = num_attention_heads
+
+ if self.hidden_size % self.num_attention_heads != 0:
+ raise ValueError(
+ f"The hidden size ({self.hidden_size}) is not a multiple of the number of attention "
+ f"heads ({self.num_attention_heads})"
+ )
+
+ self.attention_head_size = int(self.hidden_size / self.num_attention_heads)
+ self.all_head_size = self.num_attention_heads * self.attention_head_size
+
+ self.query = nn.Linear(self.hidden_size, self.all_head_size)
+ self.key = nn.Linear(self.hidden_size, self.all_head_size)
+ self.value = nn.Linear(self.hidden_size, self.all_head_size)
+
+ self.dropout = nn.Dropout(config.attention_probs_dropout_prob)
+
+ self.sr_ratio = sequence_reduction_ratio
+ if sequence_reduction_ratio > 1:
+ self.sr = nn.Conv2d(
+ hidden_size, hidden_size, kernel_size=sequence_reduction_ratio, stride=sequence_reduction_ratio
+ )
+ self.layer_norm = nn.LayerNorm(hidden_size)
+
+ def forward(
+ self,
+ hidden_states,
+ height,
+ width,
+ output_attentions=False,
+ ):
+ batch_size, seq_length, _ = hidden_states.shape
+ query_layer = (
+ self.query(hidden_states)
+ .view(batch_size, -1, self.num_attention_heads, self.attention_head_size)
+ .transpose(1, 2)
+ )
+
+ if self.sr_ratio > 1:
+ batch_size, seq_len, num_channels = hidden_states.shape
+ # Reshape to (batch_size, num_channels, height, width)
+ hidden_states = hidden_states.permute(0, 2, 1).reshape(batch_size, num_channels, height, width)
+ # Apply sequence reduction
+ hidden_states = self.sr(hidden_states)
+ # Reshape back to (batch_size, seq_len, num_channels)
+ hidden_states = hidden_states.reshape(batch_size, num_channels, -1).permute(0, 2, 1)
+ hidden_states = self.layer_norm(hidden_states)
+
+ key_layer = (
+ self.key(hidden_states)
+ .view(batch_size, -1, self.num_attention_heads, self.attention_head_size)
+ .transpose(1, 2)
+ )
+ value_layer = (
+ self.value(hidden_states)
+ .view(batch_size, -1, self.num_attention_heads, self.attention_head_size)
+ .transpose(1, 2)
+ )
+
+ # Take the dot product between "query" and "key" to get the raw attention scores.
+ attention_scores = torch.matmul(query_layer, key_layer.transpose(-1, -2))
+
+ attention_scores = attention_scores / math.sqrt(self.attention_head_size)
+
+ # Normalize the attention scores to probabilities.
+ attention_probs = nn.functional.softmax(attention_scores, dim=-1)
+
+ # This is actually dropping out entire tokens to attend to, which might
+ # seem a bit unusual, but is taken from the original Transformer paper.
+ attention_probs = self.dropout(attention_probs)
+
+ context_layer = torch.matmul(attention_probs, value_layer)
+
+ context_layer = context_layer.permute(0, 2, 1, 3).contiguous()
+ new_context_layer_shape = context_layer.size()[:-2] + (self.all_head_size,)
+ context_layer = context_layer.view(new_context_layer_shape)
+
+ outputs = (context_layer, attention_probs) if output_attentions else (context_layer,)
+
+ return outputs
+
+
+# Copied from transformers.models.segformer.modeling_segformer.SegformerSelfOutput
+class GLPNSelfOutput(nn.Module):
+ def __init__(self, config, hidden_size):
+ super().__init__()
+ self.dense = nn.Linear(hidden_size, hidden_size)
+ self.dropout = nn.Dropout(config.hidden_dropout_prob)
+
+ def forward(self, hidden_states, input_tensor):
+ hidden_states = self.dense(hidden_states)
+ hidden_states = self.dropout(hidden_states)
+ return hidden_states
+
+
+# Copied from transformers.models.segformer.modeling_segformer.SegformerAttention with Segformer->GLPN
+class GLPNAttention(nn.Module):
+ def __init__(self, config, hidden_size, num_attention_heads, sequence_reduction_ratio):
+ super().__init__()
+ self.self = GLPNEfficientSelfAttention(
+ config=config,
+ hidden_size=hidden_size,
+ num_attention_heads=num_attention_heads,
+ sequence_reduction_ratio=sequence_reduction_ratio,
+ )
+ self.output = GLPNSelfOutput(config, hidden_size=hidden_size)
+
+ def forward(self, hidden_states, height, width, output_attentions=False):
+ self_outputs = self.self(hidden_states, height, width, output_attentions)
+
+ attention_output = self.output(self_outputs[0], hidden_states)
+ outputs = (attention_output,) + self_outputs[1:] # add attentions if we output them
+ return outputs
+
+
+# Copied from transformers.models.segformer.modeling_segformer.SegformerDWConv
+class GLPNDWConv(nn.Module):
+ def __init__(self, dim=768):
+ super().__init__()
+ self.dwconv = nn.Conv2d(dim, dim, 3, 1, 1, bias=True, groups=dim)
+
+ def forward(self, hidden_states, height, width):
+ batch_size, seq_len, num_channels = hidden_states.shape
+ hidden_states = hidden_states.transpose(1, 2).view(batch_size, num_channels, height, width)
+ hidden_states = self.dwconv(hidden_states)
+ hidden_states = hidden_states.flatten(2).transpose(1, 2)
+
+ return hidden_states
+
+
+# Copied from transformers.models.segformer.modeling_segformer.SegformerMixFFN with Segformer->GLPN
+class GLPNMixFFN(nn.Module):
+ def __init__(self, config, in_features, hidden_features=None, out_features=None):
+ super().__init__()
+ out_features = out_features or in_features
+ self.dense1 = nn.Linear(in_features, hidden_features)
+ self.dwconv = GLPNDWConv(hidden_features)
+ if isinstance(config.hidden_act, str):
+ self.intermediate_act_fn = ACT2FN[config.hidden_act]
+ else:
+ self.intermediate_act_fn = config.hidden_act
+ self.dense2 = nn.Linear(hidden_features, out_features)
+ self.dropout = nn.Dropout(config.hidden_dropout_prob)
+
+ def forward(self, hidden_states, height, width):
+ hidden_states = self.dense1(hidden_states)
+ hidden_states = self.dwconv(hidden_states, height, width)
+ hidden_states = self.intermediate_act_fn(hidden_states)
+ hidden_states = self.dropout(hidden_states)
+ hidden_states = self.dense2(hidden_states)
+ hidden_states = self.dropout(hidden_states)
+ return hidden_states
+
+
+# Copied from transformers.models.segformer.modeling_segformer.SegformerLayer with Segformer->GLPN
+class GLPNLayer(nn.Module):
+ """This corresponds to the Block class in the original implementation."""
+
+ def __init__(self, config, hidden_size, num_attention_heads, drop_path, sequence_reduction_ratio, mlp_ratio):
+ super().__init__()
+ self.layer_norm_1 = nn.LayerNorm(hidden_size)
+ self.attention = GLPNAttention(
+ config,
+ hidden_size=hidden_size,
+ num_attention_heads=num_attention_heads,
+ sequence_reduction_ratio=sequence_reduction_ratio,
+ )
+ self.drop_path = GLPNDropPath(drop_path) if drop_path > 0.0 else nn.Identity()
+ self.layer_norm_2 = nn.LayerNorm(hidden_size)
+ mlp_hidden_size = int(hidden_size * mlp_ratio)
+ self.mlp = GLPNMixFFN(config, in_features=hidden_size, hidden_features=mlp_hidden_size)
+
+ def forward(self, hidden_states, height, width, output_attentions=False):
+ self_attention_outputs = self.attention(
+ self.layer_norm_1(hidden_states), # in GLPN, layernorm is applied before self-attention
+ height,
+ width,
+ output_attentions=output_attentions,
+ )
+
+ attention_output = self_attention_outputs[0]
+ outputs = self_attention_outputs[1:] # add self attentions if we output attention weights
+
+ # first residual connection (with stochastic depth)
+ attention_output = self.drop_path(attention_output)
+ hidden_states = attention_output + hidden_states
+
+ mlp_output = self.mlp(self.layer_norm_2(hidden_states), height, width)
+
+ # second residual connection (with stochastic depth)
+ mlp_output = self.drop_path(mlp_output)
+ layer_output = mlp_output + hidden_states
+
+ outputs = (layer_output,) + outputs
+
+ return outputs
+
+
+class GLPNEncoder(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+ self.config = config
+
+ # stochastic depth decay rule
+ dpr = [x.item() for x in torch.linspace(0, config.drop_path_rate, sum(config.depths), device="cpu")]
+
+ # patch embeddings
+ embeddings = []
+ for i in range(config.num_encoder_blocks):
+ embeddings.append(
+ GLPNOverlapPatchEmbeddings(
+ patch_size=config.patch_sizes[i],
+ stride=config.strides[i],
+ num_channels=config.num_channels if i == 0 else config.hidden_sizes[i - 1],
+ hidden_size=config.hidden_sizes[i],
+ )
+ )
+ self.patch_embeddings = nn.ModuleList(embeddings)
+
+ # Transformer blocks
+ blocks = []
+ cur = 0
+ for i in range(config.num_encoder_blocks):
+ # each block consists of layers
+ layers = []
+ if i != 0:
+ cur += config.depths[i - 1]
+ for j in range(config.depths[i]):
+ layers.append(
+ GLPNLayer(
+ config,
+ hidden_size=config.hidden_sizes[i],
+ num_attention_heads=config.num_attention_heads[i],
+ drop_path=dpr[cur + j],
+ sequence_reduction_ratio=config.sr_ratios[i],
+ mlp_ratio=config.mlp_ratios[i],
+ )
+ )
+ blocks.append(nn.ModuleList(layers))
+
+ self.block = nn.ModuleList(blocks)
+
+ # Layer norms
+ self.layer_norm = nn.ModuleList(
+ [nn.LayerNorm(config.hidden_sizes[i]) for i in range(config.num_encoder_blocks)]
+ )
+
+ def forward(
+ self,
+ pixel_values,
+ output_attentions=False,
+ output_hidden_states=False,
+ return_dict=True,
+ ):
+ all_hidden_states = () if output_hidden_states else None
+ all_self_attentions = () if output_attentions else None
+
+ batch_size = pixel_values.shape[0]
+
+ hidden_states = pixel_values
+ for idx, x in enumerate(zip(self.patch_embeddings, self.block, self.layer_norm)):
+ embedding_layer, block_layer, norm_layer = x
+ # first, obtain patch embeddings
+ hidden_states, height, width = embedding_layer(hidden_states)
+ # second, send embeddings through blocks
+ for i, blk in enumerate(block_layer):
+ layer_outputs = blk(hidden_states, height, width, output_attentions)
+ hidden_states = layer_outputs[0]
+ if output_attentions:
+ all_self_attentions = all_self_attentions + (layer_outputs[1],)
+ # third, apply layer norm
+ hidden_states = norm_layer(hidden_states)
+ # fourth, optionally reshape back to (batch_size, num_channels, height, width)
+ hidden_states = hidden_states.reshape(batch_size, height, width, -1).permute(0, 3, 1, 2).contiguous()
+ if output_hidden_states:
+ all_hidden_states = all_hidden_states + (hidden_states,)
+
+ if not return_dict:
+ return tuple(v for v in [hidden_states, all_hidden_states, all_self_attentions] if v is not None)
+ return BaseModelOutput(
+ last_hidden_state=hidden_states,
+ hidden_states=all_hidden_states,
+ attentions=all_self_attentions,
+ )
+
+
+@auto_docstring
+class GLPNPreTrainedModel(PreTrainedModel):
+ config: GLPNConfig
+ base_model_prefix = "glpn"
+ main_input_name = "pixel_values"
+ input_modalities = ("image",)
+ _no_split_modules = []
+
+
+@auto_docstring
+class GLPNModel(GLPNPreTrainedModel):
+ # Copied from transformers.models.segformer.modeling_segformer.SegformerModel.__init__ with Segformer->GLPN
+ def __init__(self, config):
+ super().__init__(config)
+ self.config = config
+
+ # hierarchical Transformer encoder
+ self.encoder = GLPNEncoder(config)
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @auto_docstring
+ # Copied from transformers.models.segformer.modeling_segformer.SegformerModel.forward
+ def forward(
+ self,
+ pixel_values: torch.FloatTensor,
+ output_attentions: bool | None = None,
+ output_hidden_states: bool | None = None,
+ return_dict: bool | None = None,
+ **kwargs,
+ ) -> tuple | BaseModelOutput:
+ 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.return_dict
+
+ encoder_outputs = self.encoder(
+ pixel_values,
+ output_attentions=output_attentions,
+ output_hidden_states=output_hidden_states,
+ return_dict=return_dict,
+ )
+ sequence_output = encoder_outputs[0]
+
+ if not return_dict:
+ return (sequence_output,) + encoder_outputs[1:]
+
+ return BaseModelOutput(
+ last_hidden_state=sequence_output,
+ hidden_states=encoder_outputs.hidden_states,
+ attentions=encoder_outputs.attentions,
+ )
+
+
+class GLPNSelectiveFeatureFusion(nn.Module):
+ """
+ Selective Feature Fusion module, as explained in the [paper](https://huggingface.co/papers/2201.07436) (section 3.4). This
+ module adaptively selects and integrates local and global features by attaining an attention map for each feature.
+ """
+
+ def __init__(self, in_channel=64):
+ super().__init__()
+
+ self.convolutional_layer1 = nn.Sequential(
+ nn.Conv2d(in_channels=int(in_channel * 2), out_channels=in_channel, kernel_size=3, stride=1, padding=1),
+ nn.BatchNorm2d(in_channel),
+ nn.ReLU(),
+ )
+
+ self.convolutional_layer2 = nn.Sequential(
+ nn.Conv2d(in_channels=in_channel, out_channels=int(in_channel / 2), kernel_size=3, stride=1, padding=1),
+ nn.BatchNorm2d(int(in_channel / 2)),
+ nn.ReLU(),
+ )
+
+ self.convolutional_layer3 = nn.Conv2d(
+ in_channels=int(in_channel / 2), out_channels=2, kernel_size=3, stride=1, padding=1
+ )
+
+ self.sigmoid = nn.Sigmoid()
+
+ def forward(self, local_features, global_features):
+ # concatenate features along the channel dimension
+ features = torch.cat((local_features, global_features), dim=1)
+ # pass through convolutional layers
+ features = self.convolutional_layer1(features)
+ features = self.convolutional_layer2(features)
+ features = self.convolutional_layer3(features)
+ # apply sigmoid to get two-channel attention map
+ attn = self.sigmoid(features)
+ # construct hybrid features by adding element-wise
+ hybrid_features = local_features * attn[:, 0, :, :].unsqueeze(1) + global_features * attn[
+ :, 1, :, :
+ ].unsqueeze(1)
+
+ return hybrid_features
+
+
+class GLPNDecoderStage(nn.Module):
+ def __init__(self, in_channels, out_channels):
+ super().__init__()
+ should_skip = in_channels == out_channels
+ self.convolution = nn.Conv2d(in_channels, out_channels, kernel_size=1) if not should_skip else nn.Identity()
+ self.fusion = GLPNSelectiveFeatureFusion(out_channels)
+ self.upsample = nn.Upsample(scale_factor=2, mode="bilinear", align_corners=False)
+
+ def forward(self, hidden_state, residual=None):
+ hidden_state = self.convolution(hidden_state)
+ if residual is not None:
+ hidden_state = self.fusion(hidden_state, residual)
+ hidden_state = self.upsample(hidden_state)
+
+ return hidden_state
+
+ hidden_state = self.upsample(hidden_state)
+ return hidden_state
+
+
+class GLPNDecoder(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+ # we use features from end -> start
+ reserved_hidden_sizes = config.hidden_sizes[::-1]
+ out_channels = config.decoder_hidden_size
+
+ self.stages = nn.ModuleList(
+ [GLPNDecoderStage(hidden_size, out_channels) for hidden_size in reserved_hidden_sizes]
+ )
+ # don't fuse in first stage
+ self.stages[0].fusion = None
+
+ self.final_upsample = nn.Upsample(scale_factor=2, mode="bilinear", align_corners=False)
+
+ def forward(self, hidden_states: list[torch.Tensor]) -> list[torch.Tensor]:
+ stage_hidden_states = []
+ stage_hidden_state = None
+ for hidden_state, stage in zip(hidden_states[::-1], self.stages):
+ stage_hidden_state = stage(hidden_state, stage_hidden_state)
+ stage_hidden_states.append(stage_hidden_state)
+
+ stage_hidden_states[-1] = self.final_upsample(stage_hidden_state)
+
+ return stage_hidden_states
+
+
+class SiLogLoss(nn.Module):
+ r"""
+ Implements the Scale-invariant log scale loss [Eigen et al., 2014](https://huggingface.co/papers/1406.2283).
+
+ $$L=\frac{1}{n} \sum_{i} d_{i}^{2}-\frac{1}{2 n^{2}}\left(\sum_{i} d_{i}^{2}\right)$$ where $d_{i}=\log y_{i}-\log
+ y_{i}^{*}$.
+
+ """
+
+ def __init__(self, lambd=0.5):
+ super().__init__()
+ self.lambd = lambd
+
+ def forward(self, pred, target):
+ valid_mask = (target > 0).detach()
+ diff_log = torch.log(target[valid_mask]) - torch.log(pred[valid_mask])
+ loss = torch.sqrt(torch.pow(diff_log, 2).mean() - self.lambd * torch.pow(diff_log.mean(), 2))
+
+ return loss
+
+
+class GLPNDepthEstimationHead(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+
+ self.config = config
+
+ channels = config.decoder_hidden_size
+ self.head = nn.Sequential(
+ nn.Conv2d(channels, channels, kernel_size=3, stride=1, padding=1),
+ nn.ReLU(inplace=False),
+ nn.Conv2d(channels, 1, kernel_size=3, stride=1, padding=1),
+ )
+
+ def forward(self, hidden_states: list[torch.Tensor]) -> torch.Tensor:
+ # use last features of the decoder
+ hidden_states = hidden_states[self.config.head_in_index]
+
+ hidden_states = self.head(hidden_states)
+
+ predicted_depth = torch.sigmoid(hidden_states) * self.config.max_depth
+ predicted_depth = predicted_depth.squeeze(dim=1)
+
+ return predicted_depth
+
+
+@auto_docstring(
+ custom_intro="""
+ GLPN Model transformer with a lightweight depth estimation head on top e.g. for KITTI, NYUv2.
+ """
+)
+class GLPNForDepthEstimation(GLPNPreTrainedModel):
+ def __init__(self, config):
+ super().__init__(config)
+
+ self.glpn = GLPNModel(config)
+ self.decoder = GLPNDecoder(config)
+ self.head = GLPNDepthEstimationHead(config)
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @auto_docstring
+ def forward(
+ self,
+ pixel_values: torch.FloatTensor,
+ labels: torch.FloatTensor | None = None,
+ output_attentions: bool | None = None,
+ output_hidden_states: bool | None = None,
+ return_dict: bool | None = None,
+ **kwargs,
+ ) -> tuple[torch.Tensor] | DepthEstimatorOutput:
+ r"""
+ labels (`torch.FloatTensor` of shape `(batch_size, height, width)`, *optional*):
+ Ground truth depth estimation maps for computing the loss.
+
+ Examples:
+
+ ```python
+ >>> from transformers import AutoImageProcessor, GLPNForDepthEstimation
+ >>> import torch
+ >>> import numpy as np
+ >>> from PIL import Image
+ >>> import httpx
+ >>> from io import BytesIO
+
+ >>> url = "http://images.cocodataset.org/val2017/000000039769.jpg"
+ >>> with httpx.stream("GET", url) as response:
+ ... image = Image.open(BytesIO(response.read()))
+
+ >>> image_processor = AutoImageProcessor.from_pretrained("vinvino02/glpn-kitti")
+ >>> model = GLPNForDepthEstimation.from_pretrained("vinvino02/glpn-kitti")
+
+ >>> # prepare image for the model
+ >>> inputs = image_processor(images=image, return_tensors="pt")
+
+ >>> with torch.no_grad():
+ ... outputs = model(**inputs)
+
+ >>> # interpolate to original size
+ >>> post_processed_output = image_processor.post_process_depth_estimation(
+ ... outputs,
+ ... target_sizes=[(image.height, image.width)],
+ ... )
+
+ >>> # visualize the prediction
+ >>> predicted_depth = post_processed_output[0]["predicted_depth"]
+ >>> depth = predicted_depth * 255 / predicted_depth.max()
+ >>> depth = depth.detach().cpu().numpy()
+ >>> depth = Image.fromarray(depth.astype("uint8"))
+ ```"""
+ return_dict = return_dict if return_dict is not None else self.config.return_dict
+ output_hidden_states = (
+ output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
+ )
+
+ outputs = self.glpn(
+ pixel_values,
+ output_attentions=output_attentions,
+ output_hidden_states=True, # we need the intermediate hidden states
+ return_dict=return_dict,
+ )
+
+ hidden_states = outputs.hidden_states if return_dict else outputs[1]
+
+ out = self.decoder(hidden_states)
+ predicted_depth = self.head(out)
+
+ loss = None
+ if labels is not None:
+ loss_fct = SiLogLoss()
+ loss = loss_fct(predicted_depth, labels)
+
+ if not return_dict:
+ if output_hidden_states:
+ output = (predicted_depth,) + outputs[1:]
+ else:
+ output = (predicted_depth,) + outputs[2:]
+ return ((loss,) + output) if loss is not None else output
+
+ return DepthEstimatorOutput(
+ loss=loss,
+ predicted_depth=predicted_depth,
+ hidden_states=outputs.hidden_states if output_hidden_states else None,
+ attentions=outputs.attentions,
+ )
+
+
+__all__ = ["GLPNForDepthEstimation", "GLPNLayer", "GLPNModel", "GLPNPreTrainedModel"]
diff --git a/third_party/transformers/src/transformers/models/gpt2/CONVERSION.md b/third_party/transformers/src/transformers/models/gpt2/CONVERSION.md
new file mode 100644
index 0000000000000000000000000000000000000000..fc55cb338b8161a638d81196a0a7d6d0694464b8
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/gpt2/CONVERSION.md
@@ -0,0 +1,9 @@
+Here is how to convert a GPT2 model generated outside of `transformers`
+
+* [Megatron-LM](https://github.com/NVIDIA/Megatron-LM)-generated model:
+
+ Use [convert_megatron_gpt2_checkpoint.py](../megatron_gpt2/convert_megatron_gpt2_checkpoint.py)
+
+* [big-science fork of Megatron-Deepspeed](https://github.com/bigscience-workshop/Megatron-DeepSpeed/)-generated model:
+
+ Use the instructions [here](https://github.com/bigscience-workshop/bigscience/tree/aa872e754106f6678e8a9dac8c6962404ba39a6d/train/tr1-13B-base#checkpoint-conversion-and-upload). This approach uses a set of scripts that require the use of this particular fork of Megatron-Deepspeed.
diff --git a/third_party/transformers/src/transformers/models/gpt2/__init__.py b/third_party/transformers/src/transformers/models/gpt2/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..090606b7b8372de5ecb60af00d23c275b0305de4
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/gpt2/__init__.py
@@ -0,0 +1,28 @@
+# Copyright 2024 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_gpt2 import *
+ from .modeling_gpt2 import *
+ from .tokenization_gpt2 import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/gpt2/configuration_gpt2.py b/third_party/transformers/src/transformers/models/gpt2/configuration_gpt2.py
new file mode 100644
index 0000000000000000000000000000000000000000..709e6ca86a482e560a3c8bb4aff018b269b8ad14
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/gpt2/configuration_gpt2.py
@@ -0,0 +1,106 @@
+# Copyright 2018 The OpenAI Team Authors and HuggingFace Inc. team.
+# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""OpenAI GPT-2 configuration"""
+
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...utils import auto_docstring
+
+
+@auto_docstring(checkpoint="openai-community/gpt2")
+@strict
+class GPT2Config(PreTrainedConfig):
+ r"""
+ summary_type (`string`, *optional*, defaults to `"cls_index"`):
+ Argument used when doing sequence summary, used in the models [`GPT2DoubleHeadsModel`].
+ Has to be one of the following options:
+ - `"last"`: Take the last token hidden state (like XLNet).
+ - `"first"`: Take the first token hidden state (like BERT).
+ - `"mean"`: Take the mean of all tokens hidden states.
+ - `"cls_index"`: Supply a Tensor of classification token position (like GPT/GPT-2).
+ - `"attn"`: Not implemented now, use multi-head attention.
+ summary_use_proj (`bool`, *optional*, defaults to `True`):
+ Argument used when doing sequence summary, used in the models [`GPT2DoubleHeadsModel`].
+ Whether or not to add a projection after the vector extraction.
+ summary_activation (`str`, *optional*):
+ Argument used when doing sequence summary. Used in for the multiple choice head in
+ [`GPT2DoubleHeadsModel`].
+ Pass `"tanh"` for a tanh activation to the output, any other value will result in no activation.
+ summary_proj_to_labels (`bool`, *optional*, defaults to `True`):
+ Argument used when doing sequence summary, used in the models [`GPT2DoubleHeadsModel`].
+ Whether the projection outputs should have `config.num_labels` or `config.hidden_size` classes.
+ summary_first_dropout (`float`, *optional*, defaults to 0.1):
+ Argument used when doing sequence summary, used in the models [`GPT2DoubleHeadsModel`].
+ The dropout ratio to be used after the projection and activation.
+ scale_attn_by_inverse_layer_idx (`bool`, *optional*, defaults to `False`):
+ Whether to additionally scale attention weights by `1 / layer_idx + 1`.
+ reorder_and_upcast_attn (`bool`, *optional*, defaults to `False`):
+ Whether to scale keys (K) prior to computing attention (dot-product) and upcast attention
+ dot-product/softmax to float() when training with mixed precision.
+
+ Example:
+
+ ```python
+ >>> from transformers import GPT2Config, GPT2Model
+
+ >>> # Initializing a GPT2 configuration
+ >>> configuration = GPT2Config()
+
+ >>> # Initializing a model (with random weights) from the configuration
+ >>> model = GPT2Model(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "gpt2"
+ keys_to_ignore_at_inference = ["past_key_values"]
+ attribute_map = {
+ "hidden_size": "n_embd",
+ "max_position_embeddings": "n_positions",
+ "num_attention_heads": "n_head",
+ "num_hidden_layers": "n_layer",
+ }
+
+ vocab_size: int = 50257
+ n_positions: int = 1024
+ n_embd: int = 768
+ n_layer: int = 12
+ n_head: int = 12
+ n_inner: int | None = None
+ activation_function: str = "gelu_new"
+ resid_pdrop: float | int = 0.1
+ embd_pdrop: float | int = 0.1
+ attn_pdrop: float | int = 0.1
+ layer_norm_epsilon: float = 1e-5
+ initializer_range: float = 0.02
+ summary_type: str = "cls_index"
+ summary_use_proj: bool = True
+ summary_activation: str | None = None
+ summary_proj_to_labels: bool = True
+ summary_first_dropout: float | int = 0.1
+ scale_attn_weights: bool = True
+ use_cache: bool = True
+ bos_token_id: int | None = 50256
+ eos_token_id: int | list[int] | None = 50256
+ pad_token_id: int | None = None
+ scale_attn_by_inverse_layer_idx: bool = False
+ reorder_and_upcast_attn: bool = False
+ add_cross_attention: bool = False
+ tie_word_embeddings: bool = True
+
+
+__all__ = ["GPT2Config"]
diff --git a/third_party/transformers/src/transformers/models/gpt2/convert_gpt2_original_tf_checkpoint_to_pytorch.py b/third_party/transformers/src/transformers/models/gpt2/convert_gpt2_original_tf_checkpoint_to_pytorch.py
new file mode 100644
index 0000000000000000000000000000000000000000..4c69a01eeff8067d4e808c858fdd9422cf75ca47
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/gpt2/convert_gpt2_original_tf_checkpoint_to_pytorch.py
@@ -0,0 +1,125 @@
+# Copyright 2018 The HuggingFace Inc. team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Convert OpenAI GPT checkpoint."""
+
+import argparse
+import os
+
+import torch
+
+from transformers import GPT2Config, GPT2Model
+from transformers.utils import CONFIG_NAME, WEIGHTS_NAME, logging
+
+
+logging.set_verbosity_info()
+logger = logging.get_logger(__name__)
+
+
+def load_tf_weights_in_gpt2(model, config, gpt2_checkpoint_path):
+ """Load tf checkpoints in a pytorch model"""
+ try:
+ import re
+
+ import tensorflow as tf
+ except ImportError:
+ logger.error(
+ "Loading a TensorFlow model in PyTorch, requires TensorFlow to be installed. Please see "
+ "https://www.tensorflow.org/install/ for installation instructions."
+ )
+ raise
+ tf_path = os.path.abspath(gpt2_checkpoint_path)
+ logger.info(f"Converting TensorFlow checkpoint from {tf_path}")
+ # Load weights from TF model
+ init_vars = tf.train.list_variables(tf_path)
+ names = []
+ arrays = []
+ for name, shape in init_vars:
+ logger.info(f"Loading TF weight {name} with shape {shape}")
+ array = tf.train.load_variable(tf_path, name)
+ names.append(name)
+ arrays.append(array.squeeze())
+
+ for name, array in zip(names, arrays):
+ name = name[6:] # skip "model/"
+ name = name.split("/")
+ pointer = model
+ for m_name in name:
+ if re.fullmatch(r"[A-Za-z]+\d+", m_name):
+ scope_names = re.split(r"(\d+)", m_name)
+ else:
+ scope_names = [m_name]
+ if scope_names[0] == "w" or scope_names[0] == "g":
+ pointer = getattr(pointer, "weight")
+ elif scope_names[0] == "b":
+ pointer = getattr(pointer, "bias")
+ elif scope_names[0] == "wpe" or scope_names[0] == "wte":
+ pointer = getattr(pointer, scope_names[0])
+ pointer = getattr(pointer, "weight")
+ else:
+ pointer = getattr(pointer, scope_names[0])
+ if len(scope_names) >= 2:
+ num = int(scope_names[1])
+ pointer = pointer[num]
+ try:
+ if pointer.shape != array.shape:
+ raise ValueError(f"Pointer shape {pointer.shape} and array shape {array.shape} mismatched")
+ except ValueError as e:
+ e.args += (pointer.shape, array.shape)
+ raise
+ logger.info(f"Initialize PyTorch weight {name}")
+ pointer.data = torch.from_numpy(array)
+ return model
+
+
+def convert_gpt2_checkpoint_to_pytorch(gpt2_checkpoint_path, gpt2_config_file, pytorch_dump_folder_path):
+ # Construct model
+ if gpt2_config_file == "":
+ config = GPT2Config()
+ else:
+ config = GPT2Config.from_json_file(gpt2_config_file)
+ model = GPT2Model(config)
+
+ # Load weights from numpy
+ load_tf_weights_in_gpt2(model, config, gpt2_checkpoint_path)
+
+ # Save pytorch-model
+ pytorch_weights_dump_path = pytorch_dump_folder_path + "/" + WEIGHTS_NAME
+ pytorch_config_dump_path = pytorch_dump_folder_path + "/" + CONFIG_NAME
+ print(f"Save PyTorch model to {pytorch_weights_dump_path}")
+ torch.save(model.state_dict(), pytorch_weights_dump_path)
+ print(f"Save configuration file to {pytorch_config_dump_path}")
+ with open(pytorch_config_dump_path, "w", encoding="utf-8") as f:
+ f.write(config.to_json_string())
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ # Required parameters
+ parser.add_argument(
+ "--gpt2_checkpoint_path", default=None, type=str, required=True, help="Path to the TensorFlow checkpoint path."
+ )
+ parser.add_argument(
+ "--pytorch_dump_folder_path", default=None, type=str, required=True, help="Path to the output PyTorch model."
+ )
+ parser.add_argument(
+ "--gpt2_config_file",
+ default="",
+ type=str,
+ help=(
+ "An optional config json file corresponding to the pre-trained OpenAI model. \n"
+ "This specifies the model architecture."
+ ),
+ )
+ args = parser.parse_args()
+ convert_gpt2_checkpoint_to_pytorch(args.gpt2_checkpoint_path, args.gpt2_config_file, args.pytorch_dump_folder_path)
diff --git a/third_party/transformers/src/transformers/models/gpt2/modeling_gpt2.py b/third_party/transformers/src/transformers/models/gpt2/modeling_gpt2.py
new file mode 100644
index 0000000000000000000000000000000000000000..7bb2a7cd74af1c0f245e5cadcd5402f02f797293
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/gpt2/modeling_gpt2.py
@@ -0,0 +1,1144 @@
+# Copyright 2018 The OpenAI Team Authors and HuggingFace Inc. team.
+# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""PyTorch OpenAI GPT-2 model."""
+
+import math
+from collections.abc import Callable
+from dataclasses import dataclass
+
+import torch
+from torch import nn
+from torch.nn import BCEWithLogitsLoss, CrossEntropyLoss, MSELoss
+
+from ... import initialization as init
+from ...activations import ACT2FN, get_activation
+from ...cache_utils import Cache, DynamicCache, EncoderDecoderCache
+from ...generation import GenerationMixin
+from ...masking_utils import create_bidirectional_mask, create_causal_mask
+from ...modeling_layers import GradientCheckpointingLayer
+from ...modeling_outputs import (
+ BaseModelOutputWithPastAndCrossAttentions,
+ CausalLMOutputWithCrossAttentions,
+ QuestionAnsweringModelOutput,
+ SequenceClassifierOutputWithPast,
+ TokenClassifierOutput,
+)
+from ...modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
+from ...pytorch_utils import Conv1D
+from ...utils import (
+ ModelOutput,
+ auto_docstring,
+ can_return_tuple,
+ logging,
+)
+from ...utils.generic import maybe_autocast, merge_with_config_defaults
+from ...utils.output_capturing import OutputRecorder, capture_outputs
+from .configuration_gpt2 import GPT2Config
+
+
+logger = logging.get_logger(__name__)
+
+
+def eager_attention_forward(module, query, key, value, attention_mask, scaling=None, dropout=0.0, **kwargs):
+ if scaling is None:
+ scaling = query.size(-1) ** -0.5
+
+ attn_weights = torch.matmul(query, key.transpose(-1, -2)) * scaling
+
+ if attention_mask is not None:
+ attn_weights = attn_weights + attention_mask
+
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1)
+
+ # Downcast (if necessary) back to V's dtype (if in mixed-precision) -- No-Op otherwise
+ attn_weights = attn_weights.type(value.dtype)
+ attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
+
+ attn_output = torch.matmul(attn_weights, value)
+ attn_output = attn_output.transpose(1, 2)
+
+ return attn_output, attn_weights
+
+
+class GPT2Attention(nn.Module):
+ def __init__(self, config, is_cross_attention=False, layer_idx=None):
+ super().__init__()
+ self.config = config
+ self.embed_dim = config.hidden_size
+ self.num_heads = config.num_attention_heads
+ self.head_dim = self.embed_dim // self.num_heads
+ self.split_size = self.embed_dim
+ if self.head_dim * self.num_heads != self.embed_dim:
+ raise ValueError(
+ f"`embed_dim` must be divisible by num_heads (got `embed_dim`: {self.embed_dim} and `num_heads`:"
+ f" {self.num_heads})."
+ )
+
+ self.scale_attn_weights = config.scale_attn_weights
+ self.scale_attn_by_inverse_layer_idx = config.scale_attn_by_inverse_layer_idx
+ self.reorder_and_upcast_attn = config.reorder_and_upcast_attn
+ self.is_cross_attention = is_cross_attention
+ self.layer_idx = layer_idx
+
+ # Precompute unified scaling factor (accounts for both head_dim and layer-wise scaling)
+ self.scaling = 1.0
+ if self.scale_attn_weights:
+ self.scaling = self.head_dim**-0.5
+ if self.scale_attn_by_inverse_layer_idx:
+ self.scaling /= float(self.layer_idx + 1)
+
+ if self.is_cross_attention:
+ self.c_attn = Conv1D(2 * self.embed_dim, self.embed_dim)
+ self.q_attn = Conv1D(self.embed_dim, self.embed_dim)
+ else:
+ self.c_attn = Conv1D(3 * self.embed_dim, self.embed_dim)
+ self.c_proj = Conv1D(self.embed_dim, self.embed_dim)
+
+ self.attn_dropout = nn.Dropout(config.attn_pdrop)
+ self.resid_dropout = nn.Dropout(config.resid_pdrop)
+ self.is_causal = not is_cross_attention
+
+ def _upcast_and_reordered_attn(self, query, key, value, attention_mask=None):
+ # Use `torch.baddbmm` (a bit more efficient w/ alpha param for scaling -- from Megatron-LM)
+ bsz, num_heads, q_seq_len, dk = query.size()
+ _, _, k_seq_len, _ = key.size()
+
+ # Preallocate attn_weights for `baddbmm`
+ attn_weights = torch.empty(bsz * num_heads, q_seq_len, k_seq_len, dtype=torch.float32, device=query.device)
+
+ # Upcast (turn off autocast) and reorder (Scale K by 1 / root(dk))
+ with maybe_autocast(query.device.type, enabled=False):
+ q, k = query.reshape(-1, q_seq_len, dk), key.transpose(-1, -2).reshape(-1, dk, k_seq_len)
+ attn_weights = torch.baddbmm(attn_weights, q.float(), k.float(), beta=0, alpha=self.scaling)
+ attn_weights = attn_weights.reshape(bsz, num_heads, q_seq_len, k_seq_len)
+
+ if attention_mask is not None:
+ # Apply the attention mask
+ attn_weights = attn_weights + attention_mask
+
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1)
+
+ # Downcast (if necessary) back to V's dtype (if in mixed-precision) -- No-Op if otherwise
+ if attn_weights.dtype != torch.float32:
+ raise RuntimeError("Error with upcasting, attn_weights does not have dtype torch.float32")
+ attn_weights = attn_weights.type(value.dtype)
+ attn_weights = self.attn_dropout(attn_weights)
+
+ attn_output = torch.matmul(attn_weights, value)
+ attn_output = attn_output.transpose(1, 2)
+
+ return attn_output, attn_weights
+
+ def forward(
+ self,
+ hidden_states: tuple[torch.FloatTensor] | None,
+ past_key_values: Cache | None = None,
+ attention_mask: torch.FloatTensor | None = None,
+ encoder_hidden_states: torch.Tensor | None = None,
+ encoder_attention_mask: torch.FloatTensor | None = None,
+ output_attentions: bool | None = False,
+ **kwargs,
+ ) -> tuple[torch.Tensor | tuple[torch.Tensor], ...]:
+ is_cross_attention = encoder_hidden_states is not None
+ if past_key_values is not None:
+ if isinstance(past_key_values, EncoderDecoderCache):
+ is_updated = past_key_values.is_updated.get(self.layer_idx)
+ if is_cross_attention:
+ # after the first generated id, we can subsequently re-use all key/value_layer from cache
+ curr_past_key_values = past_key_values.cross_attention_cache
+ else:
+ curr_past_key_values = past_key_values.self_attention_cache
+ else:
+ curr_past_key_values = past_key_values
+
+ if is_cross_attention:
+ if not hasattr(self, "q_attn"):
+ raise ValueError(
+ "If class is used as cross attention, the weights `q_attn` have to be defined. "
+ "Please make sure to instantiate class with `GPT2Attention(..., is_cross_attention=True)`."
+ )
+ query_states = self.q_attn(hidden_states)
+ attention_mask = encoder_attention_mask
+
+ # Try to get key/value states from cache if possible
+ if past_key_values is not None and is_updated:
+ key_states = curr_past_key_values.layers[self.layer_idx].keys
+ value_states = curr_past_key_values.layers[self.layer_idx].values
+ else:
+ key_states, value_states = self.c_attn(encoder_hidden_states).split(self.split_size, dim=2)
+ shape_kv = (*key_states.shape[:-1], -1, self.head_dim)
+ key_states = key_states.view(shape_kv).transpose(1, 2)
+ value_states = value_states.view(shape_kv).transpose(1, 2)
+ else:
+ query_states, key_states, value_states = self.c_attn(hidden_states).split(self.split_size, dim=2)
+ shape_kv = (*key_states.shape[:-1], -1, self.head_dim)
+ key_states = key_states.view(shape_kv).transpose(1, 2)
+ value_states = value_states.view(shape_kv).transpose(1, 2)
+
+ shape_q = (*query_states.shape[:-1], -1, self.head_dim)
+ query_states = query_states.view(shape_q).transpose(1, 2)
+
+ if (past_key_values is not None and not is_cross_attention) or (
+ past_key_values is not None and is_cross_attention and not is_updated
+ ):
+ key_states, value_states = curr_past_key_values.update(key_states, value_states, self.layer_idx)
+ # set flag that curr layer for cross-attn is already updated so we can re-use in subsequent calls
+ if is_cross_attention:
+ past_key_values.is_updated[self.layer_idx] = True
+
+ using_eager = self.config._attn_implementation == "eager"
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ if using_eager and self.reorder_and_upcast_attn:
+ attn_output, attn_weights = self._upcast_and_reordered_attn(
+ query_states, key_states, value_states, attention_mask
+ )
+ else:
+ attn_output, attn_weights = attention_interface(
+ self,
+ query_states,
+ key_states,
+ value_states,
+ attention_mask,
+ dropout=self.attn_dropout.p if self.training else 0.0,
+ scaling=self.scaling,
+ **kwargs,
+ )
+
+ attn_output = attn_output.reshape(*attn_output.shape[:-2], -1).contiguous()
+ attn_output = self.c_proj(attn_output)
+ attn_output = self.resid_dropout(attn_output)
+
+ return attn_output, attn_weights
+
+
+class GPT2MLP(nn.Module):
+ def __init__(self, intermediate_size, config):
+ super().__init__()
+ embed_dim = config.hidden_size
+ self.c_fc = Conv1D(intermediate_size, embed_dim)
+ self.c_proj = Conv1D(embed_dim, intermediate_size)
+ self.act = ACT2FN[config.activation_function]
+ self.dropout = nn.Dropout(config.resid_pdrop)
+
+ def forward(self, hidden_states: tuple[torch.FloatTensor] | None) -> torch.FloatTensor:
+ hidden_states = self.c_fc(hidden_states)
+ hidden_states = self.act(hidden_states)
+ hidden_states = self.c_proj(hidden_states)
+ hidden_states = self.dropout(hidden_states)
+ return hidden_states
+
+
+class GPT2Block(GradientCheckpointingLayer):
+ def __init__(self, config, layer_idx=None):
+ super().__init__()
+ hidden_size = config.hidden_size
+ inner_dim = config.n_inner if config.n_inner is not None else 4 * hidden_size
+
+ self.ln_1 = nn.LayerNorm(hidden_size, eps=config.layer_norm_epsilon)
+ self.attn = GPT2Attention(config=config, layer_idx=layer_idx)
+ self.ln_2 = nn.LayerNorm(hidden_size, eps=config.layer_norm_epsilon)
+
+ if config.add_cross_attention:
+ self.crossattention = GPT2Attention(config=config, is_cross_attention=True, layer_idx=layer_idx)
+ self.ln_cross_attn = nn.LayerNorm(hidden_size, eps=config.layer_norm_epsilon)
+
+ self.mlp = GPT2MLP(inner_dim, config)
+
+ def forward(
+ self,
+ hidden_states: tuple[torch.FloatTensor] | None,
+ past_key_values: Cache | None = None,
+ attention_mask: torch.FloatTensor | None = None,
+ encoder_hidden_states: torch.Tensor | None = None,
+ encoder_attention_mask: torch.FloatTensor | None = None,
+ use_cache: bool | None = False,
+ **kwargs,
+ ) -> torch.Tensor:
+ residual = hidden_states
+ hidden_states = self.ln_1(hidden_states)
+ attn_output, _ = self.attn(
+ hidden_states,
+ past_key_values=past_key_values,
+ attention_mask=attention_mask,
+ use_cache=use_cache,
+ **kwargs,
+ )
+ # residual connection
+ hidden_states = attn_output + residual
+
+ if encoder_hidden_states is not None:
+ # add one self-attention block for cross-attention
+ if not hasattr(self, "crossattention"):
+ raise ValueError(
+ f"If `encoder_hidden_states` are passed, {self} has to be instantiated with "
+ "cross-attention layers by setting `config.add_cross_attention=True`"
+ )
+ residual = hidden_states
+ hidden_states = self.ln_cross_attn(hidden_states)
+ cross_attn_output, _ = self.crossattention(
+ hidden_states,
+ past_key_values=past_key_values,
+ attention_mask=attention_mask,
+ encoder_hidden_states=encoder_hidden_states,
+ encoder_attention_mask=encoder_attention_mask,
+ )
+ # residual connection
+ hidden_states = residual + cross_attn_output
+
+ residual = hidden_states
+ hidden_states = self.ln_2(hidden_states)
+ feed_forward_hidden_states = self.mlp(hidden_states)
+ # residual connection
+ hidden_states = residual + feed_forward_hidden_states
+
+ return hidden_states
+
+
+# Copied from transformers.models.xlm.modeling_xlm.XLMSequenceSummary with XLM->GPT2
+class GPT2SequenceSummary(nn.Module):
+ r"""
+ Compute a single vector summary of a sequence hidden states.
+
+ Args:
+ config ([`GPT2Config`]):
+ The config used by the model. Relevant arguments in the config class of the model are (refer to the actual
+ config class of your model for the default values it uses):
+
+ - **summary_type** (`str`) -- The method to use to make this summary. Accepted values are:
+
+ - `"last"` -- Take the last token hidden state (like XLNet)
+ - `"first"` -- Take the first token hidden state (like Bert)
+ - `"mean"` -- Take the mean of all tokens hidden states
+ - `"cls_index"` -- Supply a Tensor of classification token position (GPT/GPT-2)
+ - `"attn"` -- Not implemented now, use multi-head attention
+
+ - **summary_use_proj** (`bool`) -- Add a projection after the vector extraction.
+ - **summary_proj_to_labels** (`bool`) -- If `True`, the projection outputs to `config.num_labels` classes
+ (otherwise to `config.hidden_size`).
+ - **summary_activation** (`Optional[str]`) -- Set to `"tanh"` to add a tanh activation to the output,
+ another string or `None` will add no activation.
+ - **summary_first_dropout** (`float`) -- Optional dropout probability before the projection and activation.
+ - **summary_last_dropout** (`float`)-- Optional dropout probability after the projection and activation.
+ """
+
+ def __init__(self, config: GPT2Config):
+ super().__init__()
+
+ self.summary_type = getattr(config, "summary_type", "last")
+ if self.summary_type == "attn":
+ # We should use a standard multi-head attention module with absolute positional embedding for that.
+ # Cf. https://github.com/zihangdai/xlnet/blob/master/modeling.py#L253-L276
+ # We can probably just use the multi-head attention module of PyTorch >=1.1.0
+ raise NotImplementedError
+
+ self.summary = nn.Identity()
+ if hasattr(config, "summary_use_proj") and config.summary_use_proj:
+ if hasattr(config, "summary_proj_to_labels") and config.summary_proj_to_labels and config.num_labels > 0:
+ num_classes = config.num_labels
+ else:
+ num_classes = config.hidden_size
+ self.summary = nn.Linear(config.hidden_size, num_classes)
+
+ activation_string = getattr(config, "summary_activation", None)
+ self.activation: Callable = get_activation(activation_string) if activation_string else nn.Identity()
+
+ self.first_dropout = nn.Identity()
+ if hasattr(config, "summary_first_dropout") and config.summary_first_dropout > 0:
+ self.first_dropout = nn.Dropout(config.summary_first_dropout)
+
+ self.last_dropout = nn.Identity()
+ if hasattr(config, "summary_last_dropout") and config.summary_last_dropout > 0:
+ self.last_dropout = nn.Dropout(config.summary_last_dropout)
+
+ def forward(
+ self, hidden_states: torch.FloatTensor, cls_index: torch.LongTensor | None = None
+ ) -> torch.FloatTensor:
+ """
+ Compute a single vector summary of a sequence hidden states.
+
+ Args:
+ hidden_states (`torch.FloatTensor` of shape `[batch_size, seq_len, hidden_size]`):
+ The hidden states of the last layer.
+ cls_index (`torch.LongTensor` of shape `[batch_size]` or `[batch_size, ...]` where ... are optional leading dimensions of `hidden_states`, *optional*):
+ Used if `summary_type == "cls_index"` and takes the last token of the sequence as classification token.
+
+ Returns:
+ `torch.FloatTensor`: The summary of the sequence hidden states.
+ """
+ if self.summary_type == "last":
+ output = hidden_states[:, -1]
+ elif self.summary_type == "first":
+ output = hidden_states[:, 0]
+ elif self.summary_type == "mean":
+ output = hidden_states.mean(dim=1)
+ elif self.summary_type == "cls_index":
+ if cls_index is None:
+ cls_index = torch.full_like(
+ hidden_states[..., :1, :],
+ hidden_states.shape[-2] - 1,
+ dtype=torch.long,
+ )
+ else:
+ cls_index = cls_index.unsqueeze(-1).unsqueeze(-1)
+ cls_index = cls_index.expand((-1,) * (cls_index.dim() - 1) + (hidden_states.size(-1),))
+ # shape of cls_index: (bsz, XX, 1, hidden_size) where XX are optional leading dim of hidden_states
+ output = hidden_states.gather(-2, cls_index).squeeze(-2) # shape (bsz, XX, hidden_size)
+ elif self.summary_type == "attn":
+ raise NotImplementedError
+
+ output = self.first_dropout(output)
+ output = self.summary(output)
+ output = self.activation(output)
+ output = self.last_dropout(output)
+
+ return output
+
+
+@auto_docstring
+class GPT2PreTrainedModel(PreTrainedModel):
+ config: GPT2Config
+ base_model_prefix = "transformer"
+ supports_gradient_checkpointing = True
+ _no_split_modules = ["GPT2Block"]
+ _skip_keys_device_placement = "past_key_values"
+ _supports_flash_attn = True
+ _supports_sdpa = True
+ _supports_attention_backend = True
+ _can_compile_fullgraph = True
+ _can_record_outputs = {
+ "hidden_states": GPT2Block,
+ "attentions": OutputRecorder(GPT2Attention, layer_name=".attn", index=1),
+ "cross_attentions": OutputRecorder(GPT2Attention, layer_name=".crossattention", index=1),
+ }
+
+ # No longer used as we directly use our masks instead
+ _keys_to_ignore_on_load_unexpected = ["attn.bias", "crossattention.bias"]
+
+ @torch.no_grad()
+ def _init_weights(self, module):
+ """Initialize the weights."""
+ if isinstance(module, (nn.Linear, Conv1D)):
+ init.normal_(module.weight, mean=0.0, std=self.config.initializer_range)
+ if module.bias is not None:
+ init.zeros_(module.bias)
+ elif isinstance(module, nn.Embedding):
+ init.normal_(module.weight, mean=0.0, std=self.config.initializer_range)
+ # Here we need the check explicitly, as we slice the weight in the `zeros_` call, so it looses the flag
+ if module.padding_idx is not None and not getattr(module.weight, "_is_hf_initialized", False):
+ init.zeros_(module.weight[module.padding_idx])
+ elif isinstance(module, nn.LayerNorm):
+ init.zeros_(module.bias)
+ init.ones_(module.weight)
+
+ # Reinitialize selected weights subject to the OpenAI GPT-2 Paper Scheme:
+ # > A modified initialization which accounts for the accumulation on the residual path with model depth. Scale
+ # > the weights of residual layers at initialization by a factor of 1/√N where N is the # of residual layers.
+ # > -- GPT-2 :: https://openai.com/blog/better-language-models/
+ #
+ # Reference (Megatron-LM): https://github.com/NVIDIA/Megatron-LM/blob/main/megatron/model/gpt_model.py
+ if isinstance(module, PreTrainedModel):
+ for name, p in module.named_parameters():
+ if name == "c_proj.weight":
+ # Special Scaled Initialization --> There are 2 Layer Norms per Transformer Block
+ init.normal_(p, mean=0.0, std=self.config.initializer_range / math.sqrt(2 * self.config.n_layer))
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ Base class for outputs of models predicting if two sentences are consecutive or not.
+ """
+)
+class GPT2DoubleHeadsModelOutput(ModelOutput):
+ r"""
+ loss (`torch.FloatTensor` of shape `(1,)`, *optional*, returned when `labels` is provided):
+ Language modeling loss.
+ mc_loss (`torch.FloatTensor` of shape `(1,)`, *optional*, returned when `mc_labels` is provided):
+ Multiple choice classification loss.
+ logits (`torch.FloatTensor` of shape `(batch_size, num_choices, sequence_length, config.vocab_size)`):
+ Prediction scores of the language modeling head (scores for each vocabulary token before SoftMax).
+ mc_logits (`torch.FloatTensor` of shape `(batch_size, num_choices)`):
+ Prediction scores of the multiple choice classification head (scores for each choice before SoftMax).
+ past_key_values (`Cache`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
+ It is a [`~cache_utils.Cache`] instance. For more details, see our [kv cache guide](https://huggingface.co/docs/transformers/en/kv_cache).
+
+ Contains pre-computed hidden-states (key and values in the attention blocks) that can be used (see
+ `past_key_values` input) to speed up sequential decoding.
+ """
+
+ loss: torch.FloatTensor | None = None
+ mc_loss: torch.FloatTensor | None = None
+ logits: torch.FloatTensor | None = None
+ mc_logits: torch.FloatTensor | None = None
+ past_key_values: Cache | None = None
+ hidden_states: tuple[torch.FloatTensor] | None = None
+ attentions: tuple[torch.FloatTensor] | None = None
+
+
+@auto_docstring
+class GPT2Model(GPT2PreTrainedModel):
+ def __init__(self, config):
+ super().__init__(config)
+
+ self.embed_dim = config.hidden_size
+
+ self.wte = nn.Embedding(config.vocab_size, self.embed_dim)
+ self.wpe = nn.Embedding(config.max_position_embeddings, self.embed_dim)
+
+ self.drop = nn.Dropout(config.embd_pdrop)
+ self.h = nn.ModuleList([GPT2Block(config, layer_idx=i) for i in range(config.num_hidden_layers)])
+ self.ln_f = nn.LayerNorm(self.embed_dim, eps=config.layer_norm_epsilon)
+
+ self.gradient_checkpointing = False
+ self._attn_implementation = config._attn_implementation
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ def get_input_embeddings(self):
+ return self.wte
+
+ def set_input_embeddings(self, new_embeddings):
+ self.wte = new_embeddings
+
+ @merge_with_config_defaults
+ @capture_outputs
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ attention_mask: torch.FloatTensor | None = None,
+ token_type_ids: torch.LongTensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ encoder_hidden_states: torch.Tensor | None = None,
+ encoder_attention_mask: torch.FloatTensor | None = None,
+ use_cache: bool | None = None,
+ **kwargs,
+ ) -> BaseModelOutputWithPastAndCrossAttentions:
+ r"""
+ input_ids (`torch.LongTensor` of shape `(batch_size, input_ids_length)`):
+ `input_ids_length` = `sequence_length` if `past_key_values` is `None` else
+ `past_key_values.get_seq_length()` (`sequence_length` of input past key value states). Indices of input
+ sequence tokens in the vocabulary.
+
+ If `past_key_values` is used, only `input_ids` that do not have their past calculated should be passed as
+ `input_ids`.
+
+ Indices can be obtained using [`AutoTokenizer`]. See [`PreTrainedTokenizer.encode`] and
+ [`PreTrainedTokenizer.__call__`] for details.
+
+ [What are input IDs?](../glossary#input-ids)
+ """
+ kwargs.pop("output_attentions", None)
+ kwargs.pop("output_hidden_states", None)
+
+ if input_ids is not None and inputs_embeds is not None:
+ raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time")
+ elif input_ids is not None:
+ self.warn_if_padding_and_no_attention_mask(input_ids, attention_mask)
+ input_shape = input_ids.size()
+ input_ids = input_ids.view(-1, input_shape[-1])
+ batch_size = input_ids.shape[0]
+ elif inputs_embeds is not None:
+ input_shape = inputs_embeds.size()[:-1]
+ batch_size = inputs_embeds.shape[0]
+ else:
+ raise ValueError("You have to specify either input_ids or inputs_embeds")
+
+ if token_type_ids is not None:
+ token_type_ids = token_type_ids.view(-1, input_shape[-1])
+
+ # based on pattern from src/transformers/models/whisper/modeling_whisper.py::WhisperDecoder
+ if use_cache:
+ if past_key_values is None:
+ past_key_values = DynamicCache(config=self.config)
+
+ if self.config.add_cross_attention and not isinstance(past_key_values, EncoderDecoderCache):
+ past_key_values = EncoderDecoderCache(past_key_values, DynamicCache(config=self.config))
+
+ if inputs_embeds is None:
+ inputs_embeds = self.wte(input_ids)
+
+ if position_ids is None:
+ past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
+ position_ids = torch.arange(inputs_embeds.shape[1], device=inputs_embeds.device) + past_seen_tokens
+ position_ids = position_ids.unsqueeze(0)
+
+ position_embeds = self.wpe(position_ids)
+ hidden_states = inputs_embeds + position_embeds.to(inputs_embeds.device)
+
+ # Attention mask.
+ if attention_mask is not None and attention_mask.ndim < 4:
+ attention_mask = attention_mask.view(batch_size, -1)
+
+ causal_mask = create_causal_mask(
+ config=self.config,
+ inputs_embeds=inputs_embeds,
+ attention_mask=attention_mask,
+ past_key_values=past_key_values,
+ position_ids=position_ids,
+ )
+
+ encoder_attention_mask = None
+ if encoder_hidden_states is not None:
+ encoder_attention_mask = create_bidirectional_mask(
+ config=self.config,
+ inputs_embeds=inputs_embeds,
+ attention_mask=encoder_attention_mask,
+ encoder_hidden_states=encoder_hidden_states,
+ )
+
+ if token_type_ids is not None:
+ token_type_embeds = self.wte(token_type_ids)
+ hidden_states = hidden_states + token_type_embeds
+
+ hidden_states = self.drop(hidden_states)
+
+ output_shape = (-1,) + input_shape[1:] + (hidden_states.size(-1),)
+
+ for i, block in enumerate(self.h):
+ hidden_states = block(
+ hidden_states,
+ past_key_values if not (self.gradient_checkpointing and self.training) else None,
+ causal_mask,
+ encoder_hidden_states, # as a positional argument for gradient checkpointing
+ encoder_attention_mask=encoder_attention_mask,
+ use_cache=use_cache,
+ position_ids=position_ids,
+ **kwargs,
+ )
+
+ hidden_states = self.ln_f(hidden_states)
+
+ hidden_states = hidden_states.view(output_shape)
+
+ past_key_values = past_key_values if use_cache else None
+ return BaseModelOutputWithPastAndCrossAttentions(
+ last_hidden_state=hidden_states,
+ past_key_values=past_key_values,
+ )
+
+
+@auto_docstring(
+ custom_intro="""
+ The GPT2 Model transformer with a language modeling head on top (linear layer with weights tied to the input
+ embeddings).
+ """
+)
+class GPT2LMHeadModel(GPT2PreTrainedModel, GenerationMixin):
+ _tied_weights_keys = {"lm_head.weight": "transformer.wte.weight"}
+
+ def __init__(self, config):
+ super().__init__(config)
+ self.transformer = GPT2Model(config)
+ self.lm_head = nn.Linear(config.n_embd, config.vocab_size, bias=False)
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ attention_mask: torch.FloatTensor | None = None,
+ token_type_ids: torch.LongTensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ encoder_hidden_states: torch.Tensor | None = None,
+ encoder_attention_mask: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ **kwargs,
+ ) -> CausalLMOutputWithCrossAttentions:
+ r"""
+ input_ids (`torch.LongTensor` of shape `(batch_size, input_ids_length)`):
+ `input_ids_length` = `sequence_length` if `past_key_values` is `None` else
+ `past_key_values.get_seq_length()` (`sequence_length` of input past key value states). Indices of input
+ sequence tokens in the vocabulary.
+
+ If `past_key_values` is used, only `input_ids` that do not have their past calculated should be passed as
+ `input_ids`.
+
+ Indices can be obtained using [`AutoTokenizer`]. See [`PreTrainedTokenizer.encode`] and
+ [`PreTrainedTokenizer.__call__`] for details.
+
+ [What are input IDs?](../glossary#input-ids)
+ labels (`torch.LongTensor` of shape `(batch_size, input_ids_length)`, *optional*):
+ Labels for language modeling. Note that the labels **are shifted** inside the model, i.e. you can set
+ `labels = input_ids` Indices are selected in `[-100, 0, ..., config.vocab_size]` All labels set to `-100`
+ are ignored (masked), the loss is only computed for labels in `[0, ..., config.vocab_size]`
+ """
+ transformer_outputs: BaseModelOutputWithPastAndCrossAttentions = self.transformer(
+ input_ids,
+ past_key_values=past_key_values,
+ attention_mask=attention_mask,
+ token_type_ids=token_type_ids,
+ position_ids=position_ids,
+ inputs_embeds=inputs_embeds,
+ encoder_hidden_states=encoder_hidden_states,
+ encoder_attention_mask=encoder_attention_mask,
+ use_cache=use_cache,
+ **kwargs,
+ )
+ hidden_states = transformer_outputs.last_hidden_state
+
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
+
+ loss = None
+ if labels is not None:
+ # Flatten the tokens
+ loss = self.loss_function(
+ logits,
+ labels,
+ vocab_size=self.config.vocab_size,
+ **kwargs,
+ )
+
+ return CausalLMOutputWithCrossAttentions(
+ loss=loss,
+ logits=logits,
+ past_key_values=transformer_outputs.past_key_values,
+ hidden_states=transformer_outputs.hidden_states,
+ attentions=transformer_outputs.attentions,
+ cross_attentions=transformer_outputs.cross_attentions,
+ )
+
+
+@auto_docstring(
+ custom_intro="""
+ The GPT2 Model transformer with a language modeling and a multiple-choice classification head on top e.g. for
+ RocStories/SWAG tasks. The two heads are two linear layers. The language modeling head has its weights tied to the
+ input embeddings, the classification head takes as input the input of a specified classification token index in the
+ input sequence).
+ """
+)
+class GPT2DoubleHeadsModel(GPT2PreTrainedModel, GenerationMixin):
+ _tied_weights_keys = {"lm_head.weight": "transformer.wte.weight"}
+
+ def __init__(self, config):
+ super().__init__(config)
+ config.num_labels = 1
+ self.transformer = GPT2Model(config)
+ self.lm_head = nn.Linear(config.n_embd, config.vocab_size, bias=False)
+ self.multiple_choice_head = GPT2SequenceSummary(config)
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ attention_mask: torch.FloatTensor | None = None,
+ token_type_ids: torch.LongTensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ mc_token_ids: torch.LongTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ mc_labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ **kwargs,
+ ) -> GPT2DoubleHeadsModelOutput:
+ r"""
+ input_ids (`torch.LongTensor` of shape `(batch_size, input_ids_length)`):
+ `input_ids_length` = `sequence_length` if `past_key_values` is `None` else
+ `past_key_values.get_seq_length()` (`sequence_length` of input past key value states). Indices of input
+ sequence tokens in the vocabulary.
+
+ If `past_key_values` is used, only `input_ids` that do not have their past calculated should be passed as
+ `input_ids`.
+
+ Indices can be obtained using [`AutoTokenizer`]. See [`PreTrainedTokenizer.encode`] and
+ [`PreTrainedTokenizer.__call__`] for details.
+
+ [What are input IDs?](../glossary#input-ids)
+ mc_token_ids (`torch.LongTensor` of shape `(batch_size, num_choices)`, *optional*, default to index of the last token of the input):
+ Index of the classification token in each input sequence. Selected in the range `[0, input_ids.size(-1) -
+ 1]`.
+ labels (`torch.LongTensor` of shape `(batch_size, input_ids_length)`, *optional*):
+ Labels for language modeling. Note that the labels **are shifted** inside the model, i.e. you can set
+ `labels = input_ids`. Indices are selected in `[-100, 0, ..., config.vocab_size - 1]`. All labels set to
+ `-100` are ignored (masked), the loss is only computed for labels in `[0, ..., config.vocab_size - 1]`
+ mc_labels (`torch.LongTensor` of shape `(batch_size)`, *optional*):
+ Labels for computing the multiple choice classification loss. Indices should be in `[0, ..., num_choices]`
+ where *num_choices* is the size of the second dimension of the input tensors. (see *input_ids* above)
+
+ Example:
+
+ ```python
+ >>> import torch
+ >>> from transformers import AutoTokenizer, GPT2DoubleHeadsModel
+
+ >>> tokenizer = AutoTokenizer.from_pretrained("openai-community/gpt2")
+ >>> model = GPT2DoubleHeadsModel.from_pretrained("openai-community/gpt2")
+
+ >>> # Add a [CLS] to the vocabulary (we should train it also!)
+ >>> num_added_tokens = tokenizer.add_special_tokens({"cls_token": "[CLS]"})
+ >>> # Update the model embeddings with the new vocabulary size
+ >>> embedding_layer = model.resize_token_embeddings(len(tokenizer))
+
+ >>> choices = ["Hello, my dog is cute [CLS]", "Hello, my cat is cute [CLS]"]
+ >>> encoded_choices = [tokenizer.encode(s) for s in choices]
+ >>> cls_token_location = [tokens.index(tokenizer.cls_token_id) for tokens in encoded_choices]
+
+ >>> input_ids = torch.tensor(encoded_choices).unsqueeze(0) # Batch size: 1, number of choices: 2
+ >>> mc_token_ids = torch.tensor([cls_token_location]) # Batch size: 1
+
+ >>> outputs = model(input_ids, mc_token_ids=mc_token_ids)
+ >>> lm_logits = outputs.logits
+ >>> mc_logits = outputs.mc_logits
+ ```"""
+ transformer_outputs: BaseModelOutputWithPastAndCrossAttentions = self.transformer(
+ input_ids,
+ past_key_values=past_key_values,
+ attention_mask=attention_mask,
+ token_type_ids=token_type_ids,
+ position_ids=position_ids,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ hidden_states = transformer_outputs.last_hidden_state
+
+ lm_logits = self.lm_head(hidden_states)
+ mc_logits = self.multiple_choice_head(hidden_states, mc_token_ids).squeeze(-1)
+
+ mc_loss = None
+ if mc_labels is not None:
+ loss_fct = CrossEntropyLoss()
+ mc_loss = loss_fct(mc_logits.view(-1, mc_logits.size(-1)), mc_labels.view(-1))
+ lm_loss = None
+ if labels is not None:
+ labels = labels.to(lm_logits.device)
+ shift_logits = lm_logits[..., :-1, :].contiguous()
+ shift_labels = labels[..., 1:].contiguous()
+ loss_fct = CrossEntropyLoss()
+ lm_loss = loss_fct(shift_logits.view(-1, shift_logits.size(-1)), shift_labels.view(-1))
+
+ return GPT2DoubleHeadsModelOutput(
+ loss=lm_loss,
+ mc_loss=mc_loss,
+ logits=lm_logits,
+ mc_logits=mc_logits,
+ past_key_values=transformer_outputs.past_key_values,
+ hidden_states=transformer_outputs.hidden_states,
+ attentions=transformer_outputs.attentions,
+ )
+
+
+@auto_docstring(
+ custom_intro="""
+ The GPT2 Model transformer with a sequence classification head on top (linear layer).
+
+ [`GPT2ForSequenceClassification`] uses the last token in order to do the classification, as other causal models
+ (e.g. GPT-1) do.
+
+ Since it does classification on the last token, it requires to know the position of the last token. If a
+ `pad_token_id` is defined in the configuration, it finds the last token that is not a padding token in each row. If
+ no `pad_token_id` is defined, it simply takes the last value in each row of the batch. Since it cannot guess the
+ padding tokens when `inputs_embeds` are passed instead of `input_ids`, it does the same (take the last value in
+ each row of the batch).
+ """
+)
+class GPT2ForSequenceClassification(GPT2PreTrainedModel):
+ def __init__(self, config):
+ super().__init__(config)
+ self.num_labels = config.num_labels
+ self.transformer = GPT2Model(config)
+ self.score = nn.Linear(config.n_embd, self.num_labels, bias=False)
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ attention_mask: torch.FloatTensor | None = None,
+ token_type_ids: torch.LongTensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ **kwargs,
+ ) -> SequenceClassifierOutputWithPast:
+ r"""
+ input_ids (`torch.LongTensor` of shape `(batch_size, input_ids_length)`):
+ `input_ids_length` = `sequence_length` if `past_key_values` is `None` else
+ `past_key_values.get_seq_length()` (`sequence_length` of input past key value states). Indices of input
+ sequence tokens in the vocabulary.
+
+ If `past_key_values` is used, only `input_ids` that do not have their past calculated should be passed as
+ `input_ids`.
+
+ Indices can be obtained using [`AutoTokenizer`]. See [`PreTrainedTokenizer.encode`] and
+ [`PreTrainedTokenizer.__call__`] for details.
+
+ [What are input IDs?](../glossary#input-ids)
+ labels (`torch.LongTensor` of shape `(batch_size,)`, *optional*):
+ Labels for computing the sequence classification/regression loss. Indices should be in `[0, ...,
+ config.num_labels - 1]`. If `config.num_labels == 1` a regression loss is computed (Mean-Square loss), If
+ `config.num_labels > 1` a classification loss is computed (Cross-Entropy).
+ """
+ transformer_outputs: BaseModelOutputWithPastAndCrossAttentions = self.transformer(
+ input_ids,
+ past_key_values=past_key_values,
+ attention_mask=attention_mask,
+ token_type_ids=token_type_ids,
+ position_ids=position_ids,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ **kwargs,
+ )
+ hidden_states = transformer_outputs.last_hidden_state
+ logits = self.score(hidden_states)
+
+ if input_ids is not None:
+ batch_size, sequence_length = input_ids.shape[:2]
+ else:
+ batch_size, sequence_length = inputs_embeds.shape[:2]
+
+ if self.config.pad_token_id is None and batch_size != 1:
+ raise ValueError("Cannot handle batch sizes > 1 if no padding token is defined.")
+ if self.config.pad_token_id is None:
+ last_non_pad_token = -1
+ elif input_ids is not None:
+ # To handle both left- and right- padding, we take the rightmost token that is not equal to pad_token_id
+ non_pad_mask = (input_ids != self.config.pad_token_id).to(logits.device, torch.int32)
+ token_indices = torch.arange(input_ids.shape[-1], device=logits.device, dtype=torch.int32)
+ last_non_pad_token = (token_indices * non_pad_mask).argmax(-1)
+ else:
+ last_non_pad_token = -1
+ logger.warning_once(
+ f"{self.__class__.__name__} will not detect padding tokens in `inputs_embeds`. Results may be "
+ "unexpected if using padding tokens in conjunction with `inputs_embeds.`"
+ )
+
+ pooled_logits = logits[torch.arange(batch_size, device=logits.device), last_non_pad_token]
+
+ loss = None
+ if labels is not None:
+ if self.config.problem_type is None:
+ if self.num_labels == 1:
+ self.config.problem_type = "regression"
+ elif self.num_labels > 1 and (labels.dtype == torch.long or labels.dtype == torch.int):
+ self.config.problem_type = "single_label_classification"
+ else:
+ self.config.problem_type = "multi_label_classification"
+
+ if self.config.problem_type == "regression":
+ loss_fct = MSELoss()
+ if self.num_labels == 1:
+ loss = loss_fct(pooled_logits.squeeze(), labels.squeeze())
+ else:
+ loss = loss_fct(pooled_logits, labels)
+ elif self.config.problem_type == "single_label_classification":
+ loss_fct = CrossEntropyLoss()
+ loss = loss_fct(pooled_logits.view(-1, self.num_labels), labels.view(-1))
+ elif self.config.problem_type == "multi_label_classification":
+ loss_fct = BCEWithLogitsLoss()
+ loss = loss_fct(pooled_logits, labels)
+ return SequenceClassifierOutputWithPast(
+ loss=loss,
+ logits=pooled_logits,
+ past_key_values=transformer_outputs.past_key_values,
+ hidden_states=transformer_outputs.hidden_states,
+ attentions=transformer_outputs.attentions,
+ )
+
+
+@auto_docstring
+class GPT2ForTokenClassification(GPT2PreTrainedModel):
+ def __init__(self, config):
+ super().__init__(config)
+ self.num_labels = config.num_labels
+
+ self.transformer = GPT2Model(config)
+ if hasattr(config, "classifier_dropout") and config.classifier_dropout is not None:
+ classifier_dropout = config.classifier_dropout
+ elif hasattr(config, "hidden_dropout") and config.hidden_dropout is not None:
+ classifier_dropout = config.hidden_dropout
+ else:
+ classifier_dropout = 0.1
+ self.dropout = nn.Dropout(classifier_dropout)
+ self.classifier = nn.Linear(config.hidden_size, config.num_labels)
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ attention_mask: torch.FloatTensor | None = None,
+ token_type_ids: torch.LongTensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ **kwargs,
+ ) -> TokenClassifierOutput:
+ r"""
+ input_ids (`torch.LongTensor` of shape `(batch_size, input_ids_length)`):
+ `input_ids_length` = `sequence_length` if `past_key_values` is `None` else
+ `past_key_values.get_seq_length()` (`sequence_length` of input past key value states). Indices of input
+ sequence tokens in the vocabulary.
+
+ If `past_key_values` is used, only `input_ids` that do not have their past calculated should be passed as
+ `input_ids`.
+
+ Indices can be obtained using [`AutoTokenizer`]. See [`PreTrainedTokenizer.encode`] and
+ [`PreTrainedTokenizer.__call__`] for details.
+
+ [What are input IDs?](../glossary#input-ids)
+ labels (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*):
+ Labels for computing the sequence classification/regression loss. Indices should be in `[0, ...,
+ config.num_labels - 1]`. If `config.num_labels == 1` a regression loss is computed (Mean-Square loss), If
+ `config.num_labels > 1` a classification loss is computed (Cross-Entropy).
+ """
+ transformer_outputs: BaseModelOutputWithPastAndCrossAttentions = self.transformer(
+ input_ids,
+ past_key_values=past_key_values,
+ attention_mask=attention_mask,
+ token_type_ids=token_type_ids,
+ position_ids=position_ids,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ hidden_states = transformer_outputs.last_hidden_state
+ hidden_states = self.dropout(hidden_states)
+ logits = self.classifier(hidden_states)
+
+ loss = None
+ if labels is not None:
+ labels = labels.to(logits.device)
+ loss_fct = CrossEntropyLoss()
+ loss = loss_fct(logits.view(-1, self.num_labels), labels.view(-1))
+
+ return TokenClassifierOutput(
+ loss=loss,
+ logits=logits,
+ hidden_states=transformer_outputs.hidden_states,
+ attentions=transformer_outputs.attentions,
+ )
+
+
+@auto_docstring
+class GPT2ForQuestionAnswering(GPT2PreTrainedModel):
+ def __init__(self, config):
+ super().__init__(config)
+ self.num_labels = config.num_labels
+ self.transformer = GPT2Model(config)
+ self.qa_outputs = nn.Linear(config.hidden_size, 2)
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.FloatTensor | None = None,
+ token_type_ids: torch.LongTensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ start_positions: torch.LongTensor | None = None,
+ end_positions: torch.LongTensor | None = None,
+ **kwargs,
+ ) -> QuestionAnsweringModelOutput:
+ r"""
+ input_ids (`torch.LongTensor` of shape `(batch_size, input_ids_length)`):
+ `input_ids_length` = `sequence_length` if `past_key_values` is `None` else
+ `past_key_values.get_seq_length()` (`sequence_length` of input past key value states). Indices of input
+ sequence tokens in the vocabulary.
+
+ If `past_key_values` is used, only `input_ids` that do not have their past calculated should be passed as
+ `input_ids`.
+
+ Indices can be obtained using [`AutoTokenizer`]. See [`PreTrainedTokenizer.encode`] and
+ [`PreTrainedTokenizer.__call__`] for details.
+
+ [What are input IDs?](../glossary#input-ids)
+ """
+ outputs: BaseModelOutputWithPastAndCrossAttentions = self.transformer(
+ input_ids,
+ attention_mask=attention_mask,
+ token_type_ids=token_type_ids,
+ position_ids=position_ids,
+ inputs_embeds=inputs_embeds,
+ **kwargs,
+ )
+
+ sequence_output = outputs.last_hidden_state
+
+ logits = self.qa_outputs(sequence_output)
+ start_logits, end_logits = logits.split(1, dim=-1)
+ start_logits = start_logits.squeeze(-1).contiguous()
+ end_logits = end_logits.squeeze(-1).contiguous()
+
+ total_loss = None
+ if start_positions is not None and end_positions is not None:
+ # If we are on multi-GPU, split add a dimension
+ if len(start_positions.size()) > 1:
+ start_positions = start_positions.squeeze(-1).to(start_logits.device)
+ if len(end_positions.size()) > 1:
+ end_positions = end_positions.squeeze(-1).to(end_logits.device)
+ # sometimes the start/end positions are outside our model inputs, we ignore these terms
+ ignored_index = start_logits.size(1)
+ start_positions = start_positions.clamp(0, ignored_index)
+ end_positions = end_positions.clamp(0, ignored_index)
+
+ loss_fct = CrossEntropyLoss(ignore_index=ignored_index)
+ start_loss = loss_fct(start_logits, start_positions)
+ end_loss = loss_fct(end_logits, end_positions)
+ total_loss = (start_loss + end_loss) / 2
+
+ return QuestionAnsweringModelOutput(
+ loss=total_loss,
+ start_logits=start_logits,
+ end_logits=end_logits,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ )
+
+
+__all__ = [
+ "GPT2DoubleHeadsModel",
+ "GPT2ForQuestionAnswering",
+ "GPT2ForSequenceClassification",
+ "GPT2ForTokenClassification",
+ "GPT2LMHeadModel",
+ "GPT2Model",
+ "GPT2PreTrainedModel",
+]
diff --git a/third_party/transformers/src/transformers/models/gpt2/tokenization_gpt2.py b/third_party/transformers/src/transformers/models/gpt2/tokenization_gpt2.py
new file mode 100644
index 0000000000000000000000000000000000000000..c2e789ac338066d8d8c26f1c6b528102e5e661df
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/gpt2/tokenization_gpt2.py
@@ -0,0 +1,132 @@
+# Copyright 2018 The Open AI Team Authors and The HuggingFace Inc. team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Tokenization classes for OpenAI GPT."""
+
+from tokenizers import Tokenizer, decoders, pre_tokenizers
+from tokenizers.models import BPE
+
+from ...tokenization_utils_tokenizers import AddedToken, TokenizersBackend
+from ...utils import logging
+
+
+logger = logging.get_logger(__name__)
+
+VOCAB_FILES_NAMES = {
+ "vocab_file": "vocab.json",
+ "merges_file": "merges.txt",
+}
+
+
+class GPT2Tokenizer(TokenizersBackend):
+ """
+ Construct a GPT-2 tokenizer. Based on byte-level Byte-Pair-Encoding.
+
+ This tokenizer has been trained to treat spaces like parts of the tokens (a bit like sentencepiece) so a word will
+ be encoded differently whether it is at the beginning of the sentence (without space) or not:
+
+ ```python
+ >>> from transformers import GPT2Tokenizer
+
+ >>> tokenizer = GPT2Tokenizer.from_pretrained("openai-community/gpt2")
+ >>> tokenizer("Hello world")["input_ids"]
+ [15496, 995]
+
+ >>> tokenizer(" Hello world")["input_ids"]
+ [18435, 995]
+ ```
+
+ You can get around that behavior by passing `add_prefix_space=True` when instantiating this tokenizer or when you
+ call it on some text, but since the model was not pretrained this way, it might yield a decrease in performance.
+
+
+
+ When used with `is_split_into_words=True`, this tokenizer will add a space before each word (even the first one).
+
+
+
+ This tokenizer inherits from [`TokenizersBackend`] which contains most of the main methods. Users should refer to
+ this superclass for more information regarding those methods.
+
+ Args:
+ vocab_file (`str`):
+ Path to the vocabulary file.
+ merges_file (`str`):
+ Path to the merges file.
+ errors (`str`, *optional*, defaults to `"replace"`):
+ Paradigm to follow when decoding bytes to UTF-8. See
+ [bytes.decode](https://docs.python.org/3/library/stdtypes.html#bytes.decode) for more information.
+ unk_token (`str`, *optional*, defaults to `"<|endoftext|>"`):
+ The unknown token. A token that is not in the vocabulary cannot be converted to an ID and is set to be this
+ token instead.
+ bos_token (`str`, *optional*, defaults to `"<|endoftext|>"`):
+ The beginning of sequence token.
+ eos_token (`str`, *optional*, defaults to `"<|endoftext|>"`):
+ The end of sequence token.
+ pad_token (`str`, *optional*):
+ The token used for padding, for example when batching sequences of different lengths.
+ add_prefix_space (`bool`, *optional*, defaults to `False`):
+ Whether or not to add an initial space to the input. This allows to treat the leading word just as any
+ other word. (GPT2 tokenizer detect beginning of words by the preceding space).
+ add_bos_token (`bool`, *optional*, defaults to `False`):
+ Whether or not to add an initial beginning of sentence token to the input. This allows to treat the leading
+ word just as any other word.
+ vocab (`str` or `dict[str, int]`, *optional*):
+ Custom vocabulary dictionary. If not provided, vocabulary is loaded from `vocab_file`.
+ merges (`str` or `list[str]`, *optional*):
+ Custom merges list. If not provided, merges are loaded from `merges_file`.
+ """
+
+ vocab_files_names = VOCAB_FILES_NAMES
+ model_input_names = ["input_ids", "attention_mask"]
+ model = BPE
+
+ def __init__(
+ self,
+ vocab: str | dict[str, int] | None = None,
+ merges: str | list[str] | None = None,
+ errors: str = "replace",
+ unk_token: AddedToken | str = "<|endoftext|>",
+ bos_token: AddedToken | str = "<|endoftext|>",
+ eos_token: AddedToken | str = "<|endoftext|>",
+ pad_token: AddedToken | str | None = None,
+ add_prefix_space=False,
+ **kwargs,
+ ):
+ self.add_prefix_space = add_prefix_space
+ self._vocab = vocab if vocab is not None else {}
+ self._merges = merges or []
+ self._tokenizer = Tokenizer(
+ BPE(
+ vocab=self._vocab,
+ merges=self._merges,
+ dropout=None,
+ continuing_subword_prefix="",
+ end_of_word_suffix="",
+ fuse_unk=False,
+ )
+ )
+ self._tokenizer.pre_tokenizer = pre_tokenizers.ByteLevel(add_prefix_space=add_prefix_space)
+ self._tokenizer.decoder = decoders.ByteLevel()
+ super().__init__(
+ errors=errors,
+ unk_token=unk_token,
+ bos_token=bos_token,
+ eos_token=eos_token,
+ pad_token=pad_token,
+ add_prefix_space=add_prefix_space,
+ **kwargs,
+ )
+
+
+__all__ = ["GPT2Tokenizer"]
diff --git a/third_party/transformers/src/transformers/models/gpt_neo/__init__.py b/third_party/transformers/src/transformers/models/gpt_neo/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..242a20d00d6d5acf0adf710a2eb82f667e102f6b
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/gpt_neo/__init__.py
@@ -0,0 +1,27 @@
+# Copyright 2024 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_gpt_neo import *
+ from .modeling_gpt_neo import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/gpt_neo/configuration_gpt_neo.py b/third_party/transformers/src/transformers/models/gpt_neo/configuration_gpt_neo.py
new file mode 100644
index 0000000000000000000000000000000000000000..83924dd815ba70e4e650264d45d0964b1d686c1e
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/gpt_neo/configuration_gpt_neo.py
@@ -0,0 +1,137 @@
+# Copyright 2021 The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""GPT Neo model configuration"""
+
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...utils import auto_docstring
+
+
+@auto_docstring(checkpoint="EleutherAI/gpt-neo-1.3B")
+@strict
+class GPTNeoConfig(PreTrainedConfig):
+ r"""
+ attention_types (`list`, *optional*, defaults to `[[['global', 'local'], 12]]`):
+ The type of attention for each layer in a `List` of the following format `[[["attention_type"],
+ num_layerss]]` e.g. for a 24 layer model `[[["global"], 24]]` or `[[["global", "local"], 12]]` Choose the
+ value of `attention_type` from `["global", "local"]
+ window_size (`int`, *optional*, defaults to 256):
+ The size of the sliding window for local attention.
+
+ Example:
+
+ ```python
+ >>> from transformers import GPTNeoConfig, GPTNeoModel
+
+ >>> # Initializing a GPTNeo EleutherAI/gpt-neo-1.3B style configuration
+ >>> configuration = GPTNeoConfig()
+
+ >>> # Initializing a model (with random weights) from the EleutherAI/gpt-neo-1.3B style configuration
+ >>> model = GPTNeoModel(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "gpt_neo"
+ keys_to_ignore_at_inference = ["past_key_values"]
+ attribute_map = {"num_attention_heads": "num_heads", "num_hidden_layers": "num_layers"}
+
+ vocab_size: int = 50257
+ max_position_embeddings: int = 2048
+ hidden_size: int = 2048
+ num_layers: int = 24
+ attention_types: list | tuple | None = None
+ num_heads: int = 16
+ intermediate_size: int | None = None
+ window_size: int = 256
+ activation_function: str = "gelu_new"
+ resid_dropout: float | int = 0.0
+ embed_dropout: float | int = 0.0
+ attention_dropout: float | int = 0.0
+ classifier_dropout: float | int = 0.1
+ layer_norm_epsilon: float = 1e-5
+ initializer_range: float = 0.02
+ use_cache: bool = True
+ bos_token_id: int | None = 50256
+ eos_token_id: int | list[int] | None = 50256
+ pad_token_id: int | None = None
+ tie_word_embeddings: bool = True
+
+ def __post_init__(self, **kwargs):
+ if self.attention_types is None:
+ self.attention_types = [[["global", "local"], 12]]
+ self.attention_layers = self.expand_attention_types_params(self.attention_types)
+ super().__post_init__(**kwargs)
+
+ def validate_architecture(self):
+ """Part of `@strict`-powered validation. Validates the architecture of the config."""
+ if len(self.attention_layers) != self.num_layers:
+ raise ValueError(
+ "Configuration for convolutional module is incorrect. "
+ "It is required that `len(config.attention_layers)` == `config.num_layers` "
+ f"but is `len(config.attention_layers) = {len(self.attention_layers)}`, "
+ f"`config.num_layers = {self.num_layers}`. "
+ "`config.attention_layers` is prepared using `config.attention_types`. "
+ "Please verify the value of `config.attention_types` argument."
+ )
+
+ @staticmethod
+ def expand_attention_types_params(attention_types):
+ attentions = []
+ for item in attention_types:
+ for _ in range(item[1]):
+ attentions.extend(item[0])
+ return attentions
+
+
+def custom_unfold(input, dimension, size, step):
+ """Custom torch.Tensor.unfold implementation to enable the export to ONNX."""
+ import torch
+
+ shape = input.size()
+ rank = len(shape)
+ sizedim = shape[dimension]
+
+ low_indices = torch.arange(0, sizedim, step)
+ min_length = torch.div(sizedim - size, step, rounding_mode="floor") + 1
+ indices = torch.arange(size) + low_indices[:min_length][:, None]
+
+ s = [slice(None)] * rank
+ s[dimension] = indices
+ sliced = input[s]
+
+ perm = list(range(0, rank + 1))
+ perm.append(perm.pop(dimension + 1))
+
+ return sliced.permute(perm)
+
+
+def custom_get_block_length_and_num_blocks(seq_length, window_size):
+ """
+ Custom implementation for GPTNeoAttentionMixin._get_block_length_and_num_blocks to enable the export to ONNX as
+ original implementation uses Python variables and control flow.
+ """
+ import torch
+
+ candidates = torch.arange(1, window_size)
+ remainders = torch.remainder(seq_length, candidates)
+ divisor_indices = remainders == 0
+ divisors = candidates[divisor_indices]
+ largest_divisor = torch.max(divisors)
+ return largest_divisor, torch.div(seq_length, largest_divisor, rounding_mode="floor")
+
+
+__all__ = ["GPTNeoConfig"]
diff --git a/third_party/transformers/src/transformers/models/gpt_neo/convert_gpt_neo_mesh_tf_to_pytorch.py b/third_party/transformers/src/transformers/models/gpt_neo/convert_gpt_neo_mesh_tf_to_pytorch.py
new file mode 100644
index 0000000000000000000000000000000000000000..8718885642c4c854dcecee13ff4d903f454893f6
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/gpt_neo/convert_gpt_neo_mesh_tf_to_pytorch.py
@@ -0,0 +1,155 @@
+# Copyright 2021 The Eleuther AI and HuggingFace Inc. team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Convert GPT Neo checkpoint."""
+
+import argparse
+import json
+import os
+
+import torch
+import torch.nn as nn
+
+from transformers import GPTNeoConfig, GPTNeoForCausalLM
+from transformers.utils import logging
+
+
+logging.set_verbosity_info()
+logger = logging.get_logger(__name__)
+
+
+def load_tf_weights_in_gpt_neo(model, config, gpt_neo_checkpoint_path):
+ """Load tf checkpoints in a pytorch model"""
+ try:
+ import re
+
+ import tensorflow as tf
+ except ImportError:
+ logger.error(
+ "Loading a TensorFlow model in PyTorch, requires TensorFlow to be installed. Please see "
+ "https://www.tensorflow.org/install/ for installation instructions."
+ )
+ raise
+ tf_path = os.path.abspath(gpt_neo_checkpoint_path)
+ logger.info(f"Converting TensorFlow checkpoint from {tf_path}")
+ # Load weights from TF model
+ init_vars = tf.train.list_variables(tf_path)
+ names = []
+ arrays = []
+ for name, shape in init_vars:
+ if "global_step" not in name and "adam" not in name:
+ array = tf.train.load_variable(tf_path, name)
+ array = tf.dtypes.cast(array.squeeze(), tf.float32).numpy()
+ name = name.replace("attn/q", "attn/attention/q_proj/w")
+ name = name.replace("attn/k", "attn/attention/k_proj/w")
+ name = name.replace("attn/v", "attn/attention/v_proj/w")
+ name = name.replace("attn/o", "attn/attention/out_proj/w")
+ name = name.replace("norm_1", "ln_1")
+ name = name.replace("norm_2", "ln_2")
+ name = name.replace("attn/compute_output_bias/o_b", "attn/attention/out_proj/b")
+ name = name.replace("conv1d_main/c_fc/kernel", "c_fc/w")
+ name = name.replace("conv1d_main/c_fc/bias", "c_fc/b")
+ name = name.replace("conv1d_main/c_proj/kernel", "c_proj/w")
+ name = name.replace("conv1d_main/c_proj/bias", "c_proj/b")
+
+ names.append(name)
+ arrays.append(array)
+
+ for name, array in zip(names, arrays):
+ name = name[5:] # skip "gpt2/"
+ name = name.split("/")
+ pointer = model.transformer
+ for m_name in name:
+ if re.fullmatch(r"[A-Za-z]+\d+", m_name):
+ scope_names = re.split(r"(\d+)", m_name)
+ else:
+ scope_names = [m_name]
+ if scope_names[0] == "w" or scope_names[0] == "g":
+ pointer = getattr(pointer, "weight")
+ elif scope_names[0] == "b":
+ pointer = getattr(pointer, "bias")
+ elif scope_names[0] == "wpe" or scope_names[0] == "wte":
+ pointer = getattr(pointer, scope_names[0])
+ pointer = getattr(pointer, "weight")
+ else:
+ pointer = getattr(pointer, scope_names[0])
+ if len(scope_names) >= 2:
+ num = int(scope_names[1])
+ pointer = pointer[num]
+
+ if name[-1] == "w" and name[-2] in ["out_proj", "k_proj", "q_proj", "v_proj", "c_proj", "c_fc"]:
+ array = array.transpose()
+
+ if name == ["wte"]:
+ # if vocab is padded, then trim off the padding embeddings
+ array = array[: config.vocab_size]
+
+ if pointer.shape != array.shape:
+ raise ValueError(f"Pointer shape {pointer.shape} and array shape {array.shape} mismatched {name}")
+
+ print(f"Initialize PyTorch weight {name}")
+ pointer.data = torch.from_numpy(array)
+
+ # init the final linear layer using word embeddings
+ embs = model.transformer.wte.weight
+ lin = nn.Linear(embs.size()[1], embs.size()[0], bias=False)
+ lin.weight = embs
+ model.set_output_embeddings(lin)
+ return model
+
+
+def convert_tf_checkpoint_to_pytorch(tf_checkpoint_path, config_file, pytorch_dump_path):
+ # Initialise PyTorch model
+ config_json = json.load(open(config_file, "r"))
+ config = GPTNeoConfig(
+ hidden_size=config_json["n_embd"],
+ num_layers=config_json["n_layer"],
+ num_heads=config_json["n_head"],
+ attention_types=config_json["attention_types"],
+ max_position_embeddings=config_json["n_positions"],
+ resid_dropout=config_json["res_dropout"],
+ embed_dropout=config_json["embed_dropout"],
+ attention_dropout=config_json["attn_dropout"],
+ )
+ print(f"Building PyTorch model from configuration: {config}")
+ model = GPTNeoForCausalLM(config)
+
+ # Load weights from tf checkpoint
+ load_tf_weights_in_gpt_neo(model, config, tf_checkpoint_path)
+
+ # Save pytorch-model
+ print(f"Save PyTorch model to {pytorch_dump_path}")
+ model.save_pretrained(pytorch_dump_path)
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ # Required parameters
+ parser.add_argument(
+ "--tf_checkpoint_path", default=None, type=str, required=True, help="Path to the TensorFlow checkpoint path."
+ )
+ parser.add_argument(
+ "--config_file",
+ default=None,
+ type=str,
+ required=True,
+ help=(
+ "The config json file corresponding to the pre-trained mesh-tf model. \n"
+ "This specifies the model architecture."
+ ),
+ )
+ parser.add_argument(
+ "--pytorch_dump_path", default=None, type=str, required=True, help="Path to the output PyTorch model."
+ )
+ args = parser.parse_args()
+ convert_tf_checkpoint_to_pytorch(args.tf_checkpoint_path, args.config_file, args.pytorch_dump_path)
diff --git a/third_party/transformers/src/transformers/models/gpt_neo/modeling_gpt_neo.py b/third_party/transformers/src/transformers/models/gpt_neo/modeling_gpt_neo.py
new file mode 100644
index 0000000000000000000000000000000000000000..5bf0bc584d85219efec6886b1109e88b2ed721cb
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/gpt_neo/modeling_gpt_neo.py
@@ -0,0 +1,916 @@
+# Copyright 2021 The Eleuther AI and HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""PyTorch GPT Neo model."""
+
+import torch
+from torch import nn
+from torch.nn import BCEWithLogitsLoss, CrossEntropyLoss, MSELoss
+
+from ... import initialization as init
+from ...activations import ACT2FN
+from ...cache_utils import Cache, DynamicCache
+from ...generation import GenerationMixin
+from ...masking_utils import create_causal_mask
+from ...modeling_flash_attention_utils import flash_attn_supports_top_left_mask, is_flash_attn_available
+from ...modeling_layers import GradientCheckpointingLayer
+from ...modeling_outputs import (
+ BaseModelOutputWithPast,
+ BaseModelOutputWithPastAndCrossAttentions,
+ CausalLMOutputWithCrossAttentions,
+ CausalLMOutputWithPast,
+ QuestionAnsweringModelOutput,
+ SequenceClassifierOutputWithPast,
+ TokenClassifierOutput,
+)
+from ...modeling_utils import PreTrainedModel
+from ...utils import (
+ auto_docstring,
+ logging,
+)
+from .configuration_gpt_neo import GPTNeoConfig
+
+
+if is_flash_attn_available():
+ from ...modeling_flash_attention_utils import _flash_attention_forward
+
+
+logger = logging.get_logger(__name__)
+
+
+class GPTNeoSelfAttention(nn.Module):
+ def __init__(self, config, attention_type, layer_id=None):
+ super().__init__()
+ self.config = config
+
+ max_positions = config.max_position_embeddings
+ bias = torch.tril(torch.ones((max_positions, max_positions), dtype=bool)).view(
+ 1, 1, max_positions, max_positions
+ )
+
+ # local causal self attention is a sliding window where each token can only attend to the previous
+ # window_size tokens. This is implemented by updating the causal mask such that for each token
+ # all other tokens are masked except the previous window_size tokens.
+ self.attention_type = attention_type
+ if attention_type == "local":
+ bias = torch.bitwise_xor(bias, torch.tril(bias, -config.window_size))
+
+ self.register_buffer("bias", bias, persistent=False)
+
+ self.attn_dropout = nn.Dropout(float(config.attention_dropout))
+ self.resid_dropout = nn.Dropout(float(config.resid_dropout))
+ self.is_causal = True
+ self.layer_id = layer_id
+
+ self.embed_dim = config.hidden_size
+ self.num_heads = config.num_heads
+ self.head_dim = self.embed_dim // self.num_heads
+ if self.head_dim * self.num_heads != self.embed_dim:
+ raise ValueError(
+ f"embed_dim must be divisible by num_heads (got `embed_dim`: {self.embed_dim} and `num_heads`:"
+ f" {self.num_heads})."
+ )
+
+ self.k_proj = nn.Linear(self.embed_dim, self.embed_dim, bias=False)
+ self.v_proj = nn.Linear(self.embed_dim, self.embed_dim, bias=False)
+ self.q_proj = nn.Linear(self.embed_dim, self.embed_dim, bias=False)
+ self.out_proj = nn.Linear(self.embed_dim, self.embed_dim, bias=True)
+
+ def _split_heads(self, tensor, num_heads, attn_head_size):
+ """
+ Splits hidden_size dim into attn_head_size and num_heads
+ """
+ new_shape = tensor.size()[:-1] + (num_heads, attn_head_size)
+ tensor = tensor.view(new_shape)
+ return tensor.permute(0, 2, 1, 3) # (batch, head, seq_length, head_features)
+
+ def _merge_heads(self, tensor, num_heads, attn_head_size):
+ """
+ Merges attn_head_size dim and num_attn_heads dim into hidden_size
+ """
+ tensor = tensor.permute(0, 2, 1, 3).contiguous()
+ new_shape = tensor.size()[:-2] + (num_heads * attn_head_size,)
+ return tensor.view(new_shape)
+
+ def _attn(self, query, key, value, attention_mask=None):
+ # Keep the attention weights computation in fp32 to avoid overflow issues
+ query = query.to(torch.float32)
+ key = key.to(torch.float32)
+
+ attn_weights = torch.matmul(query, key.transpose(-1, -2))
+
+ # Apply sliding window masking for local attention layers
+ query_length, key_length = query.size(-2), key.size(-2)
+ causal_mask = self.bias[:, :, key_length - query_length : key_length, :key_length]
+ mask_value = torch.finfo(attn_weights.dtype).min
+ # Need to be a tensor, otherwise we get error: `RuntimeError: expected scalar type float but found double`.
+ # Need to be on the same device, otherwise `RuntimeError: ..., x and y to be on the same device`
+ mask_value = torch.tensor(mask_value, dtype=attn_weights.dtype, device=attn_weights.device)
+ attn_weights = torch.where(causal_mask, attn_weights, mask_value)
+
+ if attention_mask is not None:
+ attn_weights = attn_weights + attention_mask
+
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1)
+ attn_weights = attn_weights.to(value.dtype)
+ attn_weights = self.attn_dropout(attn_weights)
+
+ attn_output = torch.matmul(attn_weights, value)
+
+ return attn_output, attn_weights
+
+ def forward(
+ self,
+ hidden_states,
+ attention_mask=None,
+ layer_past=None,
+ use_cache=False,
+ output_attentions=False,
+ **kwargs,
+ ):
+ query = self.q_proj(hidden_states)
+ key = self.k_proj(hidden_states)
+ value = self.v_proj(hidden_states)
+
+ query = self._split_heads(query, self.num_heads, self.head_dim)
+ key = self._split_heads(key, self.num_heads, self.head_dim)
+ value = self._split_heads(value, self.num_heads, self.head_dim)
+
+ if layer_past is not None:
+ key, value = layer_past.update(key, value, self.layer_id)
+
+ attn_output, attn_weights = self._attn(query, key, value, attention_mask)
+
+ attn_output = self._merge_heads(attn_output, self.num_heads, self.head_dim)
+ attn_output = self.out_proj(attn_output)
+ attn_output = self.resid_dropout(attn_output)
+
+ return attn_output, attn_weights
+
+
+class GPTNeoFlashAttention2(GPTNeoSelfAttention):
+ """
+ GPTNeo flash attention module. This module inherits from `GPTNeoSelfAttention` as the weights of the module stays
+ untouched. The only required change would be on the forward pass where it needs to correctly call the public API of
+ flash attention and deal with padding tokens in case the input contains any of them.
+ """
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ # TODO: Should be removed once Flash Attention for RoCm is bumped to 2.1.
+ # flash_attn<2.1 generates top-left aligned causal mask, while what is needed here is bottom-right alignment, that was made default for flash_attn>=2.1. This attribute is used to handle this difference. Reference: https://github.com/Dao-AILab/flash-attention/releases/tag/v2.1.0.
+ # Beware that with flash_attn<2.1, using q_seqlen != k_seqlen (except for the case q_seqlen == 1) produces a wrong mask (top-left).
+ self._flash_attn_uses_top_left_mask = flash_attn_supports_top_left_mask()
+
+ def forward(
+ self,
+ hidden_states,
+ attention_mask=None,
+ layer_past=None,
+ use_cache=False,
+ output_attentions=False,
+ **kwargs,
+ ):
+ bsz, _, _ = hidden_states.size()
+
+ query = self.q_proj(hidden_states)
+ key = self.k_proj(hidden_states)
+ value = self.v_proj(hidden_states)
+
+ query = self._split_heads(query, self.num_heads, self.head_dim)
+ key = self._split_heads(key, self.num_heads, self.head_dim)
+ value = self._split_heads(value, self.num_heads, self.head_dim)
+
+ if layer_past is not None:
+ key, value = layer_past.update(key, value, self.layer_id)
+
+ query_length = query.shape[2]
+ tgt_len = key.shape[2]
+
+ # Flash attention requires the input to have the shape
+ # batch_size x seq_length x head_dim x hidden_dim
+ query = query.transpose(1, 2).view(bsz, query_length, self.num_heads, self.head_dim)
+ key = key.transpose(1, 2).view(bsz, tgt_len, self.num_heads, self.head_dim)
+ value = value.transpose(1, 2).view(bsz, tgt_len, self.num_heads, self.head_dim)
+
+ attn_dropout = self.config.attention_dropout if self.training else 0.0
+
+ # In PEFT, usually we cast the layer norms in float32 for training stability reasons
+ # therefore the input hidden states gets silently casted in float32. Hence, we need
+ # cast them back in the correct dtype just to be sure everything works as expected.
+ # This might slowdown training & inference so it is recommended to not cast the LayerNorms
+ # in fp32. (LlamaRMSNorm handles it correctly)
+
+ device_type = query.device.type if query.device.type != "mps" else "cpu"
+ if query.dtype == torch.float32:
+ if torch.is_autocast_enabled(device_type):
+ target_dtype = torch.get_autocast_dtype(device_type)
+ # Handle the case where the model is quantized
+ elif hasattr(self.config, "_is_quantized"):
+ target_dtype = self.config.dtype
+ else:
+ target_dtype = self.q_proj.weight.dtype
+
+ logger.warning_once(
+ f"The input hidden states seems to be silently casted in float32, this might be related to"
+ f" the fact you have upcasted embedding or layer norm layers in float32. We will cast back the input in"
+ f" {target_dtype}."
+ )
+
+ query = query.to(target_dtype)
+ key = key.to(target_dtype)
+ value = value.to(target_dtype)
+
+ attn_output = _flash_attention_forward(
+ query,
+ key,
+ value,
+ attention_mask,
+ query_length,
+ dropout=attn_dropout,
+ softmax_scale=1.0,
+ is_causal=self.is_causal,
+ use_top_left_mask=self._flash_attn_uses_top_left_mask,
+ )
+
+ attn_weights_reshaped = attn_output.reshape(bsz, query_length, self.num_heads * self.head_dim)
+ attn_output = self.out_proj(attn_weights_reshaped)
+ attn_output = self.resid_dropout(attn_output)
+
+ return attn_output, attn_weights_reshaped
+
+
+GPT_NEO_ATTENTION_CLASSES = {
+ "eager": GPTNeoSelfAttention,
+ "flash_attention_2": GPTNeoFlashAttention2,
+}
+
+
+class GPTNeoAttention(nn.Module):
+ def __init__(self, config, layer_id=0):
+ super().__init__()
+ self.layer_id = layer_id
+ self.attention_layers = config.attention_layers
+ self.attention_type = self.attention_layers[layer_id]
+
+ if self.attention_type in ["global", "local"]:
+ self.attention = GPT_NEO_ATTENTION_CLASSES[config._attn_implementation](
+ config, self.attention_type, layer_id
+ )
+ else:
+ raise NotImplementedError(
+ "Only attn layer types 'global' and 'local' exist, but got `config.attention_layers`: "
+ f"{config.attention_layers}. Select attn layer types from ['global', 'local'] only."
+ )
+
+ def forward(
+ self,
+ hidden_states,
+ layer_past=None,
+ attention_mask=None,
+ use_cache=False,
+ output_attentions=False,
+ **kwargs,
+ ):
+ return self.attention(
+ hidden_states,
+ attention_mask=attention_mask,
+ layer_past=layer_past,
+ use_cache=use_cache,
+ output_attentions=output_attentions,
+ )
+
+
+class GPTNeoMLP(nn.Module):
+ def __init__(self, intermediate_size, config): # in MLP: intermediate_size= 4 * hidden_size
+ super().__init__()
+ embed_dim = config.hidden_size
+ self.c_fc = nn.Linear(embed_dim, intermediate_size)
+ self.c_proj = nn.Linear(intermediate_size, embed_dim)
+ self.act = ACT2FN[config.activation_function]
+ self.dropout = nn.Dropout(float(config.resid_dropout))
+
+ def forward(self, hidden_states):
+ hidden_states = self.c_fc(hidden_states)
+ hidden_states = self.act(hidden_states)
+ hidden_states = self.c_proj(hidden_states)
+ hidden_states = self.dropout(hidden_states)
+ return hidden_states
+
+
+class GPTNeoBlock(GradientCheckpointingLayer):
+ def __init__(self, config, layer_id=None):
+ super().__init__()
+ hidden_size = config.hidden_size
+ inner_dim = config.intermediate_size if config.intermediate_size is not None else 4 * hidden_size
+ self.ln_1 = nn.LayerNorm(hidden_size, eps=config.layer_norm_epsilon)
+ self.attn = GPTNeoAttention(config, layer_id)
+ self.ln_2 = nn.LayerNorm(hidden_size, eps=config.layer_norm_epsilon)
+ self.mlp = GPTNeoMLP(inner_dim, config)
+
+ def forward(
+ self,
+ hidden_states,
+ layer_past=None,
+ attention_mask=None,
+ use_cache=False,
+ output_attentions=False,
+ **kwargs,
+ ):
+ residual = hidden_states
+ hidden_states = self.ln_1(hidden_states)
+ attn_output, attn_weights = self.attn(
+ hidden_states,
+ layer_past=layer_past,
+ attention_mask=attention_mask,
+ use_cache=use_cache,
+ output_attentions=output_attentions,
+ )
+
+ # residual connection
+ hidden_states = attn_output + residual
+
+ residual = hidden_states
+ hidden_states = self.ln_2(hidden_states)
+ feed_forward_hidden_states = self.mlp(hidden_states)
+ # residual connection
+ hidden_states = residual + feed_forward_hidden_states
+
+ return hidden_states, attn_weights
+
+
+@auto_docstring
+class GPTNeoPreTrainedModel(PreTrainedModel):
+ config: GPTNeoConfig
+ base_model_prefix = "transformer"
+ supports_gradient_checkpointing = True
+ _no_split_modules = ["GPTNeoBlock"]
+ _skip_keys_device_placement = "past_key_values"
+ _supports_flash_attn = True
+ _can_compile_fullgraph = False # TODO: needs a hybrid cache
+
+ def _init_weights(self, module):
+ super()._init_weights(module)
+ if isinstance(module, GPTNeoSelfAttention):
+ max_positions = module.config.max_position_embeddings
+ bias = torch.tril(torch.ones((max_positions, max_positions), dtype=bool)).view(
+ 1, 1, max_positions, max_positions
+ )
+ if module.attention_type == "local":
+ bias = torch.bitwise_xor(bias, torch.tril(bias, -module.config.window_size))
+ init.copy_(module.bias, bias)
+
+
+@auto_docstring
+class GPTNeoModel(GPTNeoPreTrainedModel):
+ def __init__(self, config):
+ super().__init__(config)
+
+ self.embed_dim = config.hidden_size
+ self.wte = nn.Embedding(config.vocab_size, self.embed_dim)
+ self.wpe = nn.Embedding(config.max_position_embeddings, self.embed_dim)
+ self.drop = nn.Dropout(float(config.embed_dropout))
+ self.h = nn.ModuleList([GPTNeoBlock(config, layer_id=i) for i in range(config.num_layers)])
+ self.ln_f = nn.LayerNorm(self.embed_dim, eps=config.layer_norm_epsilon)
+
+ self.gradient_checkpointing = False
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ def get_input_embeddings(self):
+ return self.wte
+
+ def set_input_embeddings(self, new_embeddings):
+ self.wte = new_embeddings
+
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.Tensor | None = None,
+ past_key_values: Cache | None = None,
+ attention_mask: torch.Tensor | None = None,
+ token_type_ids: torch.Tensor | None = None,
+ position_ids: torch.Tensor | None = None,
+ inputs_embeds: torch.Tensor | None = None,
+ use_cache: bool | None = None,
+ output_attentions: bool | None = None,
+ output_hidden_states: bool | None = None,
+ return_dict: bool | None = None,
+ **kwargs,
+ ) -> tuple[torch.Tensor] | BaseModelOutputWithPastAndCrossAttentions:
+ r"""
+ input_ids (`torch.LongTensor` of shape `(batch_size, input_ids_length)`):
+ `input_ids_length` = `sequence_length` if `past_key_values` is `None` else
+ `past_key_values.get_seq_length()` (`sequence_length` of input past key value states). Indices of input
+ sequence tokens in the vocabulary.
+
+ If `past_key_values` is used, only `input_ids` that do not have their past calculated should be passed as
+ `input_ids`.
+
+ Indices can be obtained using [`AutoTokenizer`]. See [`PreTrainedTokenizer.encode`] and
+ [`PreTrainedTokenizer.__call__`] for details.
+
+ [What are input IDs?](../glossary#input-ids)
+ """
+ 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
+ )
+ use_cache = use_cache if use_cache is not None else self.config.use_cache
+ return_dict = return_dict if return_dict is not None else self.config.return_dict
+
+ if (input_ids is None) ^ (inputs_embeds is not None):
+ raise ValueError("You must specify exactly one of input_ids or inputs_embeds")
+
+ if self.gradient_checkpointing and self.training:
+ if use_cache:
+ logger.warning_once(
+ "`use_cache=True` is incompatible with gradient checkpointing. Setting `use_cache=False`..."
+ )
+ use_cache = False
+
+ if inputs_embeds is None:
+ inputs_embeds = self.wte(input_ids)
+
+ if use_cache and past_key_values is None:
+ past_key_values = DynamicCache(config=self.config)
+
+ if position_ids is None:
+ past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
+ position_ids = torch.arange(inputs_embeds.shape[1], device=inputs_embeds.device) + past_seen_tokens
+ position_ids = position_ids.unsqueeze(0)
+
+ causal_mask = create_causal_mask(
+ config=self.config,
+ inputs_embeds=inputs_embeds,
+ attention_mask=attention_mask,
+ past_key_values=past_key_values,
+ position_ids=position_ids,
+ )
+
+ position_embeds = self.wpe(position_ids)
+ hidden_states = inputs_embeds + position_embeds
+
+ seq_length = inputs_embeds.shape[1]
+ if token_type_ids is not None:
+ token_type_ids = token_type_ids.view(-1, seq_length)
+ token_type_embeds = self.wte(token_type_ids)
+ hidden_states = hidden_states + token_type_embeds
+
+ hidden_states = self.drop(hidden_states)
+ output_shape = (-1, seq_length, hidden_states.size(-1))
+
+ all_self_attentions = () if output_attentions else None
+ all_hidden_states = () if output_hidden_states else None
+ for i, block in enumerate(self.h):
+ if output_hidden_states:
+ all_hidden_states = all_hidden_states + (hidden_states,)
+
+ outputs = block(
+ hidden_states,
+ layer_past=past_key_values,
+ attention_mask=causal_mask,
+ use_cache=use_cache,
+ output_attentions=output_attentions,
+ )
+
+ hidden_states = outputs[0]
+ if output_attentions:
+ all_self_attentions = all_self_attentions + (outputs[1],)
+
+ hidden_states = self.ln_f(hidden_states)
+
+ hidden_states = hidden_states.view(output_shape)
+ # Add last hidden state
+ if output_hidden_states:
+ all_hidden_states = all_hidden_states + (hidden_states,)
+
+ if not return_dict:
+ return tuple(
+ v for v in [hidden_states, past_key_values, all_hidden_states, all_self_attentions] if v is not None
+ )
+
+ return BaseModelOutputWithPast(
+ last_hidden_state=hidden_states,
+ past_key_values=past_key_values,
+ hidden_states=all_hidden_states,
+ attentions=all_self_attentions,
+ )
+
+
+@auto_docstring(
+ custom_intro="""
+ The GPT Neo Model transformer with a language modeling head on top (linear layer with weights tied to the input
+ embeddings).
+ """
+)
+class GPTNeoForCausalLM(GPTNeoPreTrainedModel, GenerationMixin):
+ _tied_weights_keys = {"lm_head.weight": "transformer.wte.weight"}
+
+ def __init__(self, config):
+ super().__init__(config)
+ self.transformer = GPTNeoModel(config)
+ self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.Tensor | None = None,
+ past_key_values: Cache | None = None,
+ attention_mask: torch.Tensor | None = None,
+ token_type_ids: torch.Tensor | None = None,
+ position_ids: torch.Tensor | None = None,
+ inputs_embeds: torch.Tensor | None = None,
+ labels: torch.Tensor | None = None,
+ use_cache: bool | None = None,
+ output_attentions: bool | None = None,
+ output_hidden_states: bool | None = None,
+ return_dict: bool | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ **kwargs,
+ ) -> tuple[torch.Tensor] | CausalLMOutputWithCrossAttentions:
+ r"""
+ input_ids (`torch.LongTensor` of shape `(batch_size, input_ids_length)`):
+ `input_ids_length` = `sequence_length` if `past_key_values` is `None` else
+ `past_key_values.get_seq_length()` (`sequence_length` of input past key value states). Indices of input
+ sequence tokens in the vocabulary.
+
+ If `past_key_values` is used, only `input_ids` that do not have their past calculated should be passed as
+ `input_ids`.
+
+ Indices can be obtained using [`AutoTokenizer`]. See [`PreTrainedTokenizer.encode`] and
+ [`PreTrainedTokenizer.__call__`] for details.
+
+ [What are input IDs?](../glossary#input-ids)
+ labels (`torch.LongTensor` of shape `(batch_size, input_ids_length)`, *optional*):
+ Labels for language modeling. Note that the labels **are shifted** inside the model, i.e. you can set
+ `labels = input_ids` Indices are selected in `[-100, 0, ..., config.vocab_size]` All labels set to `-100`
+ are ignored (masked), the loss is only computed for labels in `[0, ..., config.vocab_size]`
+ """
+ return_dict = return_dict if return_dict is not None else self.config.return_dict
+
+ transformer_outputs = self.transformer(
+ input_ids,
+ past_key_values=past_key_values,
+ attention_mask=attention_mask,
+ token_type_ids=token_type_ids,
+ position_ids=position_ids,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ output_attentions=output_attentions,
+ output_hidden_states=output_hidden_states,
+ return_dict=return_dict,
+ **kwargs,
+ )
+
+ hidden_states = transformer_outputs[0]
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(logits=logits, labels=labels, vocab_size=self.config.vocab_size, **kwargs)
+
+ if not return_dict:
+ output = (logits,) + transformer_outputs[1:]
+ return ((loss,) + output) if loss is not None else output
+
+ return CausalLMOutputWithPast(
+ loss=loss,
+ logits=logits,
+ past_key_values=transformer_outputs.past_key_values,
+ hidden_states=transformer_outputs.hidden_states,
+ attentions=transformer_outputs.attentions,
+ )
+
+
+@auto_docstring(
+ custom_intro="""
+ The GPTNeo Model transformer with a sequence classification head on top (linear layer).
+
+ [`GPTNeoForSequenceClassification`] uses the last token in order to do the classification, as other causal models
+ (e.g. GPT-1) do.
+
+ Since it does classification on the last token, it requires to know the position of the last token. If a
+ `pad_token_id` is defined in the configuration, it finds the last token that is not a padding token in each row. If
+ no `pad_token_id` is defined, it simply takes the last value in each row of the batch. Since it cannot guess the
+ padding tokens when `inputs_embeds` are passed instead of `input_ids`, it does the same (take the last value in
+ each row of the batch).
+ """
+)
+class GPTNeoForSequenceClassification(GPTNeoPreTrainedModel):
+ def __init__(self, config):
+ super().__init__(config)
+ self.num_labels = config.num_labels
+ self.transformer = GPTNeoModel(config)
+ self.score = nn.Linear(config.hidden_size, self.num_labels, bias=False)
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.Tensor | None = None,
+ past_key_values: Cache | None = None,
+ attention_mask: torch.Tensor | None = None,
+ token_type_ids: torch.Tensor | None = None,
+ position_ids: torch.Tensor | None = None,
+ inputs_embeds: torch.Tensor | None = None,
+ labels: torch.Tensor | None = None,
+ use_cache: bool | None = None,
+ output_attentions: bool | None = None,
+ output_hidden_states: bool | None = None,
+ return_dict: bool | None = None,
+ **kwargs,
+ ) -> tuple[torch.Tensor] | SequenceClassifierOutputWithPast:
+ r"""
+ input_ids (`torch.LongTensor` of shape `(batch_size, input_ids_length)`):
+ `input_ids_length` = `sequence_length` if `past_key_values` is `None` else
+ `past_key_values.get_seq_length()` (`sequence_length` of input past key value states). Indices of input
+ sequence tokens in the vocabulary.
+
+ If `past_key_values` is used, only `input_ids` that do not have their past calculated should be passed as
+ `input_ids`.
+
+ Indices can be obtained using [`AutoTokenizer`]. See [`PreTrainedTokenizer.encode`] and
+ [`PreTrainedTokenizer.__call__`] for details.
+
+ [What are input IDs?](../glossary#input-ids)
+ labels (`torch.LongTensor` of shape `(batch_size,)`, *optional*):
+ Labels for computing the sequence classification/regression loss. Indices should be in `[0, ...,
+ config.num_labels - 1]`. If `config.num_labels == 1` a regression loss is computed (Mean-Square loss), If
+ `config.num_labels > 1` a classification loss is computed (Cross-Entropy).
+ """
+ return_dict = return_dict if return_dict is not None else self.config.return_dict
+
+ transformer_outputs = self.transformer(
+ input_ids,
+ past_key_values=past_key_values,
+ attention_mask=attention_mask,
+ token_type_ids=token_type_ids,
+ position_ids=position_ids,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ output_attentions=output_attentions,
+ output_hidden_states=output_hidden_states,
+ return_dict=return_dict,
+ )
+ hidden_states = transformer_outputs[0]
+ logits = self.score(hidden_states)
+
+ if input_ids is not None:
+ batch_size, sequence_length = input_ids.shape[:2]
+ else:
+ batch_size, sequence_length = inputs_embeds.shape[:2]
+
+ if self.config.pad_token_id is None and batch_size != 1:
+ raise ValueError("Cannot handle batch sizes > 1 if no padding token is defined.")
+ if self.config.pad_token_id is None:
+ last_non_pad_token = -1
+ elif input_ids is not None:
+ # To handle both left- and right- padding, we take the rightmost token that is not equal to pad_token_id
+ non_pad_mask = (input_ids != self.config.pad_token_id).to(logits.device, torch.int32)
+ token_indices = torch.arange(input_ids.shape[-1], device=logits.device, dtype=torch.int32)
+ last_non_pad_token = (token_indices * non_pad_mask).argmax(-1)
+ else:
+ last_non_pad_token = -1
+ logger.warning_once(
+ f"{self.__class__.__name__} will not detect padding tokens in `inputs_embeds`. Results may be "
+ "unexpected if using padding tokens in conjunction with `inputs_embeds.`"
+ )
+
+ pooled_logits = logits[torch.arange(batch_size, device=logits.device), last_non_pad_token]
+
+ loss = None
+ if labels is not None:
+ if self.config.problem_type is None:
+ if self.num_labels == 1:
+ self.config.problem_type = "regression"
+ elif self.num_labels > 1 and (labels.dtype == torch.long or labels.dtype == torch.int):
+ self.config.problem_type = "single_label_classification"
+ else:
+ self.config.problem_type = "multi_label_classification"
+
+ if self.config.problem_type == "regression":
+ loss_fct = MSELoss()
+ if self.num_labels == 1:
+ loss = loss_fct(pooled_logits.squeeze(), labels.squeeze())
+ else:
+ loss = loss_fct(pooled_logits, labels)
+ elif self.config.problem_type == "single_label_classification":
+ loss_fct = CrossEntropyLoss()
+ loss = loss_fct(pooled_logits.view(-1, self.num_labels), labels.view(-1))
+ elif self.config.problem_type == "multi_label_classification":
+ loss_fct = BCEWithLogitsLoss()
+ loss = loss_fct(pooled_logits, labels)
+ if not return_dict:
+ output = (pooled_logits,) + transformer_outputs[1:]
+ return ((loss,) + output) if loss is not None else output
+
+ return SequenceClassifierOutputWithPast(
+ loss=loss,
+ logits=pooled_logits,
+ past_key_values=transformer_outputs.past_key_values,
+ hidden_states=transformer_outputs.hidden_states,
+ attentions=transformer_outputs.attentions,
+ )
+
+
+@auto_docstring
+class GPTNeoForTokenClassification(GPTNeoPreTrainedModel):
+ def __init__(self, config):
+ super().__init__(config)
+ self.num_labels = config.num_labels
+
+ self.transformer = GPTNeoModel(config)
+ self.dropout = nn.Dropout(config.classifier_dropout)
+ self.classifier = nn.Linear(config.hidden_size, config.num_labels)
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ attention_mask: torch.FloatTensor | None = None,
+ token_type_ids: torch.LongTensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ output_attentions: bool | None = None,
+ output_hidden_states: bool | None = None,
+ return_dict: bool | None = None,
+ **kwargs,
+ ) -> tuple | TokenClassifierOutput:
+ r"""
+ input_ids (`torch.LongTensor` of shape `(batch_size, input_ids_length)`):
+ `input_ids_length` = `sequence_length` if `past_key_values` is `None` else
+ `past_key_values.get_seq_length()` (`sequence_length` of input past key value states). Indices of input
+ sequence tokens in the vocabulary.
+
+ If `past_key_values` is used, only `input_ids` that do not have their past calculated should be passed as
+ `input_ids`.
+
+ Indices can be obtained using [`AutoTokenizer`]. See [`PreTrainedTokenizer.encode`] and
+ [`PreTrainedTokenizer.__call__`] for details.
+
+ [What are input IDs?](../glossary#input-ids)
+ labels (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*):
+ Labels for computing the sequence classification/regression loss. Indices should be in `[0, ...,
+ config.num_labels - 1]`. If `config.num_labels == 1` a regression loss is computed (Mean-Square loss), If
+ `config.num_labels > 1` a classification loss is computed (Cross-Entropy).
+ """
+ return_dict = return_dict if return_dict is not None else self.config.return_dict
+
+ transformer_outputs = self.transformer(
+ input_ids,
+ past_key_values=past_key_values,
+ attention_mask=attention_mask,
+ token_type_ids=token_type_ids,
+ position_ids=position_ids,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ output_attentions=output_attentions,
+ output_hidden_states=output_hidden_states,
+ return_dict=return_dict,
+ )
+
+ hidden_states = transformer_outputs[0]
+ hidden_states = self.dropout(hidden_states)
+ logits = self.classifier(hidden_states)
+
+ loss = None
+ if labels is not None:
+ labels = labels.to(logits.device)
+ loss_fct = CrossEntropyLoss()
+ loss = loss_fct(logits.view(-1, self.num_labels), labels.view(-1))
+
+ if not return_dict:
+ output = (logits,) + transformer_outputs[2:]
+ return ((loss,) + output) if loss is not None else output
+
+ return TokenClassifierOutput(
+ loss=loss,
+ logits=logits,
+ hidden_states=transformer_outputs.hidden_states,
+ attentions=transformer_outputs.attentions,
+ )
+
+
+@auto_docstring
+class GPTNeoForQuestionAnswering(GPTNeoPreTrainedModel):
+ def __init__(self, config):
+ super().__init__(config)
+ self.num_labels = config.num_labels
+ self.transformer = GPTNeoModel(config)
+ self.qa_outputs = nn.Linear(config.hidden_size, 2)
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.FloatTensor | None = None,
+ token_type_ids: torch.LongTensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ start_positions: torch.LongTensor | None = None,
+ end_positions: torch.LongTensor | None = None,
+ output_attentions: bool | None = None,
+ output_hidden_states: bool | None = None,
+ return_dict: bool | None = None,
+ **kwargs,
+ ) -> tuple | QuestionAnsweringModelOutput:
+ r"""
+ input_ids (`torch.LongTensor` of shape `(batch_size, input_ids_length)`):
+ `input_ids_length` = `sequence_length` if `past_key_values` is `None` else
+ `past_key_values.get_seq_length()` (`sequence_length` of input past key value states). Indices of input
+ sequence tokens in the vocabulary.
+
+ If `past_key_values` is used, only `input_ids` that do not have their past calculated should be passed as
+ `input_ids`.
+
+ Indices can be obtained using [`AutoTokenizer`]. See [`PreTrainedTokenizer.encode`] and
+ [`PreTrainedTokenizer.__call__`] for details.
+
+ [What are input IDs?](../glossary#input-ids)
+ """
+ return_dict = return_dict if return_dict is not None else self.config.return_dict
+
+ outputs = self.transformer(
+ input_ids,
+ attention_mask=attention_mask,
+ token_type_ids=token_type_ids,
+ position_ids=position_ids,
+ inputs_embeds=inputs_embeds,
+ output_attentions=output_attentions,
+ output_hidden_states=output_hidden_states,
+ return_dict=return_dict,
+ )
+
+ sequence_output = outputs[0]
+
+ logits = self.qa_outputs(sequence_output)
+ start_logits, end_logits = logits.split(1, dim=-1)
+ start_logits = start_logits.squeeze(-1).contiguous()
+ end_logits = end_logits.squeeze(-1).contiguous()
+
+ total_loss = None
+ if start_positions is not None and end_positions is not None:
+ # If we are on multi-GPU, split add a dimension
+ if len(start_positions.size()) > 1:
+ start_positions = start_positions.squeeze(-1)
+ if len(end_positions.size()) > 1:
+ end_positions = end_positions.squeeze(-1)
+ # sometimes the start/end positions are outside our model inputs, we ignore these terms
+ ignored_index = start_logits.size(1)
+ start_positions = start_positions.clamp(0, ignored_index)
+ end_positions = end_positions.clamp(0, ignored_index)
+
+ loss_fct = CrossEntropyLoss(ignore_index=ignored_index)
+ start_loss = loss_fct(start_logits, start_positions)
+ end_loss = loss_fct(end_logits, end_positions)
+ total_loss = (start_loss + end_loss) / 2
+
+ if not return_dict:
+ output = (start_logits, end_logits) + outputs[2:]
+ return ((total_loss,) + output) if total_loss is not None else output
+
+ return QuestionAnsweringModelOutput(
+ loss=total_loss,
+ start_logits=start_logits,
+ end_logits=end_logits,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ )
+
+
+__all__ = [
+ "GPTNeoForCausalLM",
+ "GPTNeoForQuestionAnswering",
+ "GPTNeoForSequenceClassification",
+ "GPTNeoForTokenClassification",
+ "GPTNeoModel",
+ "GPTNeoPreTrainedModel",
+]
diff --git a/third_party/transformers/src/transformers/models/hiera/__init__.py b/third_party/transformers/src/transformers/models/hiera/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..841f13be4c0d2f48f54eecc916acd826395449af
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/hiera/__init__.py
@@ -0,0 +1,27 @@
+# Copyright 2024 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_hiera import *
+ from .modeling_hiera import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/hiera/configuration_hiera.py b/third_party/transformers/src/transformers/models/hiera/configuration_hiera.py
new file mode 100644
index 0000000000000000000000000000000000000000..43ee69d4845e02b30a2f3289e7550a3870e38ba3
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/hiera/configuration_hiera.py
@@ -0,0 +1,122 @@
+# Copyright 2024 The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Hiera model configuration"""
+
+from huggingface_hub.dataclasses import strict
+
+from ...backbone_utils import BackboneConfigMixin
+from ...configuration_utils import PreTrainedConfig
+from ...utils import auto_docstring
+
+
+@auto_docstring(checkpoint="facebook/hiera-base-224")
+@strict
+class HieraConfig(BackboneConfigMixin, PreTrainedConfig):
+ r"""
+ patch_stride (`list(int)`, *optional*, defaults to `[4, 4]`):
+ The stride of the patch.
+ patch_padding (`list(int)`, *optional*, defaults to `[3, 3]`):
+ The padding of the patch.
+ num_heads (`list(int)`, *optional*, defaults to `[1, 2, 4, 8]`):
+ Number of attention heads in each layer of the Transformer encoder.
+ embed_dim_multiplier (`float`, *optional*, defaults to 2.0):
+ The multiplier to the dimensionality of patch embedding in each layer of the Transformer encoder.
+ num_query_pool (`int`, *optional*, defaults to 3):
+ The number of query pool stages.
+ query_stride (`list(int)`, *optional*, defaults to `[2, 2]`):
+ The stride of the query pool.
+ masked_unit_size (`list(int)`, *optional*, defaults to `[8, 8]`):
+ The size of the masked unit.
+ masked_unit_attention (`list(bool)`, *optional*, defaults to `[True, True, False, False]`):
+ Whether to use masked unit attention in each layer of the Transformer encoder.
+ layer_norm_init (`float`, *optional*, defaults to 1.0):
+ The initial weight value for layer normalization layers.
+ decoder_depth (`int`, *optional*):
+ Depth of the decoder for MAE pretraining.
+ normalize_pixel_loss (`bool`, *optional*, defaults to `True`):
+ Whether to normalize the pixel loss by the number of pixels.
+ mask_ratio (`float`, *optional*, defaults to 0.6):
+ The ratio of masked tokens in the input.
+
+ Example:
+
+ ```python
+ >>> from transformers import HieraConfig, HieraModel
+
+ >>> # Initializing a Hiera hiera-base-patch16-224 style configuration
+ >>> configuration = HieraConfig()
+
+ >>> # Initializing a model (with random weights) from the hiera-base-patch16-224 style configuration
+ >>> model = HieraModel(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "hiera"
+
+ attribute_map = {"num_hidden_layers": "num_layers"}
+
+ embed_dim: int = 96
+ image_size: list[int] | tuple[int, ...] = (224, 224)
+ patch_size: list[int] | tuple[int, ...] = (7, 7)
+ patch_stride: list[int] | tuple[int, ...] = (4, 4)
+ patch_padding: list[int] | tuple[int, ...] = (3, 3)
+ mlp_ratio: float = 4.0
+ depths: list[int] | tuple[int, ...] = (2, 3, 16, 3)
+ num_heads: list[int] | tuple[int, ...] = (1, 2, 4, 8)
+ embed_dim_multiplier: float | int = 2.0
+ num_query_pool: int = 3
+ query_stride: list[int] | tuple[int, ...] = (2, 2)
+ masked_unit_size: list[int] | tuple[int, ...] = (8, 8)
+ masked_unit_attention: list[bool] | tuple[bool, ...] = (True, True, False, False)
+ drop_path_rate: float | int = 0.0
+ num_channels: int = 3
+ hidden_act: str = "gelu"
+ initializer_range: float = 0.02
+ layer_norm_init: float = 1.0
+ layer_norm_eps: float = 1e-6
+ decoder_hidden_size: int | None = None
+ decoder_depth: int | None = None
+ decoder_num_heads: int | None = None
+ normalize_pixel_loss: bool | None = True
+ mask_ratio: float = 0.6
+ _out_features: list[str] | None = None
+ _out_indices: list[int] | None = None
+
+ def __post_init__(self, **kwargs):
+ # we set the hidden_size attribute in order to make Hiera work with VisionEncoderDecoderModel
+ # this indicates the channel dimension after the last stage of the model
+ self.hidden_size = int(self.embed_dim * self.embed_dim_multiplier ** (len(self.depths) - 1))
+ self.stage_names = ["stem"] + [f"stage{idx}" for idx in range(1, len(self.depths) + 1)]
+ self.set_output_features_output_indices(
+ out_indices=kwargs.pop("out_indices", None), out_features=kwargs.pop("out_features", None)
+ )
+ super().__post_init__(**kwargs)
+
+ def validate_architecture(self):
+ """Part of `@strict`-powered validation. Validates the architecture of the config."""
+ if self.masked_unit_size[0] % self.query_stride[0] ** (len(self.depths) - 1) != 0:
+ raise ValueError(
+ f"masked_unit_size[0] ({self.masked_unit_size[0]}) must be divisible by query_stride[0] ({self.query_stride[0]}) "
+ f"raised to the power of the number of layers ({len(self.depths) - 1})"
+ )
+
+ if self.num_query_pool >= len(self.depths):
+ raise ValueError(
+ f"num_query_pool ({self.num_query_pool}) must be less than the number of layers ({len(self.depths)})"
+ )
+
+
+__all__ = ["HieraConfig"]
diff --git a/third_party/transformers/src/transformers/models/hiera/convert_hiera_to_hf.py b/third_party/transformers/src/transformers/models/hiera/convert_hiera_to_hf.py
new file mode 100644
index 0000000000000000000000000000000000000000..c2a0a9075cc1873eafab55f3acded7a430782a88
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/hiera/convert_hiera_to_hf.py
@@ -0,0 +1,371 @@
+# Copyright 2024 The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Convert Hiera checkpoints from the original repository.
+
+URL: https://github.com/facebookresearch/hiera
+"""
+
+import argparse
+import json
+import math
+from io import BytesIO
+
+import httpx
+import torch
+from huggingface_hub import hf_hub_download
+from PIL import Image
+from torchvision import transforms
+
+from transformers import BitImageProcessor, HieraConfig, HieraForImageClassification, HieraForPreTraining, HieraModel
+from transformers.image_utils import IMAGENET_DEFAULT_MEAN, IMAGENET_DEFAULT_STD
+from transformers.utils import logging
+
+
+logging.set_verbosity_info()
+logger = logging.get_logger(__name__)
+
+
+# here we list all keys to be renamed (original name on the left, our name on the right)
+def create_rename_keys(config: HieraConfig, base_model: bool, mae_model: bool):
+ rename_keys = []
+ # fmt: off
+ num_stages = len(config.depths)
+ # embedding dimensions for input and stages
+ dims = [config.embed_dim] + [int(config.embed_dim * config.embed_dim_multiplier**i) for i in range(num_stages)]
+
+ global_layer_idx = 0
+ for stage_idx in range(num_stages):
+ dim_in = dims[stage_idx]
+ dim_out = dims[stage_idx + 1]
+ for layer_idx in range(config.depths[stage_idx]):
+ rename_keys.append((f"blocks.{global_layer_idx}.norm1.weight", f"hiera.encoder.stages.{stage_idx}.layers.{layer_idx}.layernorm_before.weight"))
+ rename_keys.append((f"blocks.{global_layer_idx}.norm1.bias", f"hiera.encoder.stages.{stage_idx}.layers.{layer_idx}.layernorm_before.bias"))
+ rename_keys.append((f"blocks.{global_layer_idx}.attn.qkv.weight", f"hiera.encoder.stages.{stage_idx}.layers.{layer_idx}.attn.qkv.weight"))
+ rename_keys.append((f"blocks.{global_layer_idx}.attn.qkv.bias", f"hiera.encoder.stages.{stage_idx}.layers.{layer_idx}.attn.qkv.bias"))
+ rename_keys.append((f"blocks.{global_layer_idx}.attn.proj.weight", f"hiera.encoder.stages.{stage_idx}.layers.{layer_idx}.attn.proj.weight"))
+ rename_keys.append((f"blocks.{global_layer_idx}.attn.proj.bias", f"hiera.encoder.stages.{stage_idx}.layers.{layer_idx}.attn.proj.bias"))
+ rename_keys.append((f"blocks.{global_layer_idx}.norm2.weight", f"hiera.encoder.stages.{stage_idx}.layers.{layer_idx}.layernorm_after.weight"))
+ rename_keys.append((f"blocks.{global_layer_idx}.norm2.bias", f"hiera.encoder.stages.{stage_idx}.layers.{layer_idx}.layernorm_after.bias"))
+ rename_keys.append((f"blocks.{global_layer_idx}.mlp.fc1.weight", f"hiera.encoder.stages.{stage_idx}.layers.{layer_idx}.mlp.fc1.weight"))
+ rename_keys.append((f"blocks.{global_layer_idx}.mlp.fc1.bias", f"hiera.encoder.stages.{stage_idx}.layers.{layer_idx}.mlp.fc1.bias"))
+ rename_keys.append((f"blocks.{global_layer_idx}.mlp.fc2.weight", f"hiera.encoder.stages.{stage_idx}.layers.{layer_idx}.mlp.fc2.weight"))
+ rename_keys.append((f"blocks.{global_layer_idx}.mlp.fc2.bias", f"hiera.encoder.stages.{stage_idx}.layers.{layer_idx}.mlp.fc2.bias"))
+
+ # projection layer only for the first layer of each stage boundary (except the first stage)
+ if dim_out != dim_in and layer_idx == 0:
+ rename_keys.append((f"blocks.{global_layer_idx}.proj.weight", f"hiera.encoder.stages.{stage_idx}.layers.{layer_idx}.proj.weight"))
+ rename_keys.append((f"blocks.{global_layer_idx}.proj.bias", f"hiera.encoder.stages.{stage_idx}.layers.{layer_idx}.proj.bias"))
+
+ global_layer_idx += 1
+
+ # projection layer + position embeddings
+ rename_keys.extend(
+ [
+ ("patch_embed.proj.weight", "hiera.embeddings.patch_embeddings.projection.weight"),
+ ("patch_embed.proj.bias", "hiera.embeddings.patch_embeddings.projection.bias")
+ ]
+ )
+
+ rename_keys.append(("pos_embed", "hiera.embeddings.position_embeddings"))
+
+ if base_model:
+ # layernorm + pooler
+ rename_keys.extend([("norm.weight", "pooler.layernorm.weight"), ("norm.bias", "pooler.layernorm.bias")])
+ # if just the base model, we should remove "hiera" from all keys that start with "hiera"
+ rename_keys = [(pair[0], pair[1][6:]) if pair[1].startswith("hiera") else pair for pair in rename_keys]
+ elif mae_model:
+ rename_keys.extend(
+ [
+ ("encoder_norm.weight", "encoder_norm.weight"),
+ ("encoder_norm.bias", "encoder_norm.bias"),
+ ("mask_token", "decoder.mask_token"),
+ ("decoder_pos_embed", "decoder.decoder_position_embeddings"),
+ ("decoder_norm.weight", "decoder.decoder_norm.weight"),
+ ("decoder_norm.bias", "decoder.decoder_norm.bias"),
+ ("decoder_pred.weight", "decoder.decoder_pred.weight"),
+ ("decoder_pred.bias", "decoder.decoder_pred.bias"),
+ ("decoder_embed.weight", "decoder.decoder_embeddings.weight"),
+ ("decoder_embed.bias", "decoder.decoder_embeddings.bias")
+ ]
+ )
+ for i in range(config.decoder_depth):
+ rename_keys.extend(
+ [
+ (f"decoder_blocks.{i}.norm1.weight", f"decoder.decoder_block.layers.{i}.layernorm_before.weight"),
+ (f"decoder_blocks.{i}.norm1.bias", f"decoder.decoder_block.layers.{i}.layernorm_before.bias"),
+ (f"decoder_blocks.{i}.attn.qkv.weight", f"decoder.decoder_block.layers.{i}.attn.qkv.weight"),
+ (f"decoder_blocks.{i}.attn.qkv.bias", f"decoder.decoder_block.layers.{i}.attn.qkv.bias"),
+ (f"decoder_blocks.{i}.attn.proj.weight", f"decoder.decoder_block.layers.{i}.attn.proj.weight"),
+ (f"decoder_blocks.{i}.attn.proj.bias", f"decoder.decoder_block.layers.{i}.attn.proj.bias"),
+ (f"decoder_blocks.{i}.norm2.weight", f"decoder.decoder_block.layers.{i}.layernorm_after.weight"),
+ (f"decoder_blocks.{i}.norm2.bias", f"decoder.decoder_block.layers.{i}.layernorm_after.bias"),
+ (f"decoder_blocks.{i}.mlp.fc1.weight", f"decoder.decoder_block.layers.{i}.mlp.fc1.weight"),
+ (f"decoder_blocks.{i}.mlp.fc1.bias", f"decoder.decoder_block.layers.{i}.mlp.fc1.bias"),
+ (f"decoder_blocks.{i}.mlp.fc2.weight", f"decoder.decoder_block.layers.{i}.mlp.fc2.weight"),
+ (f"decoder_blocks.{i}.mlp.fc2.bias", f"decoder.decoder_block.layers.{i}.mlp.fc2.bias"),
+ ]
+ )
+ for i in range(config.num_query_pool):
+ rename_keys.extend(
+ [
+ (f"multi_scale_fusion_heads.{i}.weight", f"multiscale_fusion.multi_scale_fusion_heads.{i}.weight"),
+ (f"multi_scale_fusion_heads.{i}.bias", f"multiscale_fusion.multi_scale_fusion_heads.{i}.bias")
+ ]
+ )
+ else:
+ # layernorm + classification head
+ rename_keys.extend(
+ [
+ ("norm.weight", "hiera.pooler.layernorm.weight"),
+ ("norm.bias", "hiera.pooler.layernorm.bias"),
+ ("head.projection.weight", "classifier.weight"),
+ ("head.projection.bias", "classifier.bias"),
+ ]
+ )
+ # fmt: on
+ return rename_keys
+
+
+def remove_classification_head_(state_dict):
+ ignore_keys = ["head.projection.weight", "head.projection.bias"]
+ for k in ignore_keys:
+ state_dict.pop(k, None)
+
+
+def rename_key(dct, old, new):
+ val = dct.pop(old)
+ dct[new] = val
+
+
+# We will verify our results on an image of cute cats
+def prepare_img():
+ url = "http://images.cocodataset.org/val2017/000000039769.jpg"
+ with httpx.stream("GET", url) as response:
+ image = Image.open(BytesIO(response.read()))
+ return image
+
+
+def get_labels_for_classifier(model_name: str) -> tuple[dict[int, str], dict[str, int], int]:
+ repo_id = "huggingface/label-files"
+
+ filename = "imagenet-1k-id2label.json"
+
+ id2label = json.load(open(hf_hub_download(repo_id, filename, repo_type="dataset"), "r"))
+ id2label = {int(k): v for k, v in id2label.items()}
+ label2id = {v: k for k, v in id2label.items()}
+ num_labels = len(id2label)
+
+ return id2label, label2id, num_labels
+
+
+def get_hiera_config(model_name: str, base_model: bool, mae_model: bool) -> HieraConfig:
+ if model_name == "hiera-tiny-224":
+ config = HieraConfig(depths=[1, 2, 7, 2])
+ elif model_name == "hiera-small-224":
+ config = HieraConfig(depths=[1, 2, 11, 2])
+ elif model_name == "hiera-base-224":
+ config = HieraConfig()
+ elif model_name == "hiera-base-plus-224":
+ config = HieraConfig(embed_dim=112, num_heads=[2, 4, 8, 16])
+ elif model_name == "hiera-large-224":
+ config = HieraConfig(embed_dim=144, num_heads=[2, 4, 8, 16], depths=[2, 6, 36, 4])
+ elif model_name == "hiera-huge-224":
+ config = HieraConfig(embed_dim=256, num_heads=[4, 8, 16, 32], depths=[2, 6, 36, 4])
+ else:
+ raise ValueError(f"Unrecognized model name: {model_name}")
+
+ if base_model:
+ pass
+ elif mae_model:
+ config.num_query_pool = 2
+ config.decoder_hidden_size = 512
+ config.decoder_depth = 8
+ config.decoder_num_heads = 16
+ # Table 3b from Hiera: A Hierarchical Vision Transformer without the Bells-and-Whistles
+ config.mask_ratio = 0.6
+ else:
+ id2label, label2id, num_labels = get_labels_for_classifier(model_name)
+ config.id2label = id2label
+ config.label2id = label2id
+ config.num_labels = num_labels
+
+ return config
+
+
+@torch.no_grad()
+def convert_hiera_checkpoint(args):
+ model_name = args.model_name
+ base_model = args.base_model
+ pytorch_dump_folder_path = args.pytorch_dump_folder_path
+ push_to_hub = args.push_to_hub
+ mae_model = args.mae_model
+
+ config = get_hiera_config(model_name, base_model, mae_model)
+
+ # Load original hiera model
+ original_model_name = model_name.replace("-", "_")
+ original_model_name = f"mae_{original_model_name}" if mae_model else original_model_name
+
+ original_checkpoint_name = "mae_in1k_ft_in1k" if not (base_model or mae_model) else "mae_in1k"
+
+ original_model = torch.hub.load(
+ "facebookresearch/hiera",
+ model=original_model_name,
+ pretrained=True,
+ checkpoint=original_checkpoint_name,
+ )
+
+ original_model.eval()
+ original_state_dict = original_model.state_dict()
+ # Don't need to remove head for MAE because original implementation doesn't have it on MAE
+ if base_model:
+ remove_classification_head_(original_state_dict)
+
+ # # Rename keys
+ new_state_dict = original_state_dict.copy()
+ rename_keys = create_rename_keys(config, base_model, mae_model)
+
+ for src, dest in rename_keys:
+ rename_key(new_state_dict, src, dest)
+
+ # Load HF hiera model
+ if base_model:
+ model = HieraModel(config)
+ elif mae_model:
+ model = HieraForPreTraining(config)
+ else:
+ model = HieraForImageClassification(config)
+
+ model.eval()
+
+ missing_keys, unexpected_keys = model.load_state_dict(new_state_dict, strict=False)
+ print("Missing keys:", missing_keys)
+ print("Unexpected keys:", unexpected_keys)
+
+ input_image = prepare_img()
+
+ original_image_preprocessor = transforms.Compose(
+ [
+ transforms.Resize(int((256 / 224) * 224), interpolation=transforms.functional.InterpolationMode.BICUBIC),
+ transforms.CenterCrop(224),
+ transforms.ToTensor(),
+ transforms.Normalize(IMAGENET_DEFAULT_MEAN, IMAGENET_DEFAULT_STD),
+ ]
+ )
+
+ image_processor = BitImageProcessor(
+ image_mean=IMAGENET_DEFAULT_MEAN, image_std=IMAGENET_DEFAULT_STD, size={"shortest_edge": 256}
+ )
+ inputs = image_processor(images=input_image, return_tensors="pt")
+
+ expected_pixel_values = original_image_preprocessor(input_image).unsqueeze(0)
+
+ input_image = prepare_img()
+
+ inputs = image_processor(images=input_image, return_tensors="pt")
+ expected_pixel_values = original_image_preprocessor(input_image).unsqueeze(0)
+ assert torch.allclose(inputs.pixel_values, expected_pixel_values, atol=1e-4)
+ print("Pixel values look good!")
+ print(f"{inputs.pixel_values[0, :3, :3, :3]=}")
+
+ # If is MAE we pass a noise to generate a random mask
+ mask_spatial_shape = [
+ i // s // ms for i, s, ms in zip(config.image_size, config.patch_stride, config.masked_unit_size)
+ ]
+ num_windows = math.prod(mask_spatial_shape)
+ torch.manual_seed(2)
+ noise = torch.rand(1, num_windows)
+ outputs = model(**inputs) if not mae_model else model(noise=noise, **inputs)
+ # original implementation returns logits.softmax(dim=-1)
+
+ if base_model:
+ expected_prob, expected_intermediates = original_model(expected_pixel_values, return_intermediates=True)
+ expected_last_hidden = expected_intermediates[-1]
+ batch_size, _, _, hidden_dim = expected_last_hidden.shape
+ expected_last_hidden = expected_last_hidden.reshape(batch_size, -1, hidden_dim)
+ assert torch.allclose(outputs.last_hidden_state, expected_last_hidden, atol=1e-3)
+ print("Base Model looks good as hidden states match original implementation!")
+ print(f"{outputs.last_hidden_state[0, :3, :3]=}")
+ elif mae_model:
+ # get mask from noise to be able to compare outputs
+ mask, _ = model.hiera.embeddings.patch_embeddings.random_masking(expected_pixel_values, noise)
+ expected_loss, _, _, _ = original_model(expected_pixel_values, mask=mask.bool())
+ assert torch.allclose(outputs.loss, expected_loss, atol=1e-3)
+ print("MAE Model looks good as loss matches original implementation!")
+ else:
+ expected_prob = original_model(expected_pixel_values)
+ assert torch.allclose(outputs.logits.softmax(dim=-1), expected_prob, atol=1e-3)
+ print("Classifier looks good as probs match original implementation")
+ print(f"{outputs.logits[:, :5]=}")
+
+ if pytorch_dump_folder_path is not None:
+ print(f"Saving model and processor for {model_name} to {pytorch_dump_folder_path}")
+ model.save_pretrained(pytorch_dump_folder_path)
+ image_processor.save_pretrained(pytorch_dump_folder_path)
+
+ if push_to_hub:
+ hub_name = model_name
+ if base_model:
+ hub_name = model_name
+ elif mae_model:
+ hub_name = f"{model_name}-mae"
+ else:
+ hub_name = f"{model_name}-in1k"
+ repo_id = f"EduardoPacheco/{hub_name}"
+ print(f"Pushing model and processor for {model_name} to hub at {repo_id}")
+ model.push_to_hub(repo_id)
+ image_processor.push_to_hub(repo_id)
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ # Required parameters
+ parser.add_argument(
+ "--model-name",
+ default="hiera-tiny-224",
+ type=str,
+ choices=[
+ "hiera-tiny-224",
+ "hiera-small-224",
+ "hiera-base-224",
+ "hiera-base-plus-224",
+ "hiera-large-224",
+ "hiera-huge-224",
+ ],
+ help="Name of the Hiera model you'd like to convert.",
+ )
+ parser.add_argument(
+ "--pytorch-dump-folder_path", default=None, type=str, help="Path to the output PyTorch model directory."
+ )
+ parser.add_argument(
+ "--verify-logits",
+ action="store_true",
+ help="Whether or not to verify the logits against the original implementation.",
+ )
+ parser.add_argument(
+ "--push-to-hub",
+ action="store_true",
+ help="Whether or not to push the converted model to the Hugging Face hub.",
+ )
+ parser.add_argument(
+ "--base-model",
+ action="store_true",
+ help="Whether to only convert the base model (no projection head weights).",
+ )
+ parser.add_argument(
+ "--mae-model", action="store_true", help="Whether to convert to MAE checkpoint to HieraForPreTraining."
+ )
+
+ args = parser.parse_args()
+ convert_hiera_checkpoint(args)
diff --git a/third_party/transformers/src/transformers/models/hiera/modeling_hiera.py b/third_party/transformers/src/transformers/models/hiera/modeling_hiera.py
new file mode 100644
index 0000000000000000000000000000000000000000..59386c69b21131837aae723ece9d223bb920dd14
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/hiera/modeling_hiera.py
@@ -0,0 +1,1404 @@
+# Copyright 2024 Meta and The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""PyTorch Hiera model."""
+
+import math
+from dataclasses import dataclass
+
+import torch
+from torch import nn
+
+from ... import initialization as init
+from ...activations import ACT2FN
+from ...backbone_utils import BackboneMixin, filter_output_hidden_states
+from ...modeling_layers import GradientCheckpointingLayer
+from ...modeling_outputs import (
+ BackboneOutput,
+ BaseModelOutput,
+ BaseModelOutputWithPooling,
+ ImageClassifierOutput,
+ ModelOutput,
+)
+from ...modeling_utils import PreTrainedModel
+from ...utils import auto_docstring, logging, torch_int
+from ...utils.generic import can_return_tuple
+from .configuration_hiera import HieraConfig
+
+
+logger = logging.get_logger(__name__)
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ Hiera encoder's outputs, with potential hidden states and attentions.
+ """
+)
+class HieraEncoderOutput(ModelOutput):
+ r"""
+ reshaped_hidden_states (`tuple(torch.FloatTensor)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
+ Tuple of `torch.FloatTensor` (one for the output of the embeddings + one for the output of each stage) of
+ shape `(batch_size, height, width, hidden_size)`. These are the reshaped and re-rolled hidden states of the model.
+
+ Hidden-states of the model at the output of each layer plus the initial embedding outputs reshaped to
+ include the spatial dimensions.
+ """
+
+ last_hidden_state: torch.FloatTensor | None = None
+ hidden_states: tuple[torch.FloatTensor, ...] | None = None
+ attentions: tuple[torch.FloatTensor, ...] | None = None
+ reshaped_hidden_states: tuple[torch.FloatTensor, ...] | None = None
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ Hiera model's outputs that also contains a pooling of the last hidden states.
+ """
+)
+class HieraModelOutput(ModelOutput):
+ r"""
+ pooler_output (`torch.FloatTensor` of shape `(batch_size, hidden_size)`, *optional*, returned when `add_pooling_layer=True` is passed):
+ Average pooling of the last layer hidden-state.
+ bool_masked_pos (`torch.BoolTensor` of shape `(batch_size, sequence_length)`):
+ Tensor indicating which patches are masked (0) and which are not (1).
+ ids_restore (`torch.LongTensor` of shape `(batch_size, sequence_length)`):
+ Tensor containing the original index of the (shuffled) masked patches.
+ reshaped_hidden_states (`tuple(torch.FloatTensor)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
+ Tuple of `torch.FloatTensor` (one for the output of the embeddings + one for the output of each stage) of
+ shape `(batch_size, height, width, hidden_size)`. These are the reshaped and re-rolled hidden states of the model.
+
+ Hidden-states of the model at the output of each layer plus the initial embedding outputs reshaped to
+ include the spatial dimensions.
+ """
+
+ last_hidden_state: torch.FloatTensor | None = None
+ pooler_output: torch.FloatTensor | None = None
+ bool_masked_pos: torch.BoolTensor | None = None
+ ids_restore: torch.LongTensor | None = None
+ hidden_states: tuple[torch.FloatTensor, ...] | None = None
+ attentions: tuple[torch.FloatTensor, ...] | None = None
+ reshaped_hidden_states: tuple[torch.FloatTensor, ...] | None = None
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ Hiera image classification outputs.
+ """
+)
+class HieraForImageClassificationOutput(ImageClassifierOutput):
+ r"""
+ loss (`torch.FloatTensor` of shape `(1,)`, `optional`):
+ Loss value for the training task.
+ logits (`torch.FloatTensor` of shape `(batch_size, num_labels)`):
+ Prediction scores of the classification head (logits of the output layer).
+ hidden_states (`tuple(torch.FloatTensor)`, `optional`):
+ Tuple of `torch.FloatTensor` (one for the output of the embeddings + one for the output of each stage) of
+ shape `(batch_size, sequence_length, hidden_size)`. These are the unrolled hidden states of the model.
+
+ Hidden-states of the model at the output of each layer plus the initial embedding outputs.
+ attentions (`tuple(torch.FloatTensor)`, `optional`):
+ Tuple of `torch.FloatTensor` (one for each stage) of shape `(batch_size, num_heads, sequence_length,
+ sequence_length)`.
+
+ Attentions weights after the attention softmax, used to compute the weighted average in the self-attention
+ heads.
+ reshaped_hidden_states (`tuple(torch.FloatTensor)`, `optional`):
+ Tuple of `torch.FloatTensor` (one for the output of the embeddings + one for the output of each stage) of
+ shape `(batch_size, height, width, hidden_size)`. These are the reshaped and re-rolled hidden states of the model.
+
+ Hidden-states of the model at the output of each layer plus the initial embedding outputs reshaped to
+ include the spatial dimensions.
+ """
+
+ loss: torch.FloatTensor | None = None
+ logits: torch.FloatTensor | None = None
+ hidden_states: tuple[torch.FloatTensor, ...] | None = None
+ attentions: tuple[torch.FloatTensor, ...] | None = None
+ reshaped_hidden_states: tuple[torch.FloatTensor, ...] | None = None
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ Class for HieraForPreTraining's outputs, with potential hidden states and attentions.
+ """
+)
+class HieraForPreTrainingOutput(ModelOutput):
+ r"""
+ loss (`torch.FloatTensor` of shape `(1,)`):
+ Pixel reconstruction loss.
+ logits (`torch.FloatTensor` of shape `(batch_size, sequence_length, patch_size ** 2 * num_channels)`):
+ Pixel reconstruction logits.
+ bool_masked_pos (`torch.BoolTensor` of shape `(batch_size, sequence_length)`):
+ Tensor indicating which patches are masked (0) and which are not (1).
+ ids_restore (`torch.LongTensor` of shape `(batch_size, sequence_length)`):
+ Tensor containing the original index of the (shuffled) masked patches.
+ reshaped_hidden_states (`tuple(torch.FloatTensor)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
+ Tuple of `torch.FloatTensor` (one for the output of the embeddings + one for the output of each layer) of
+ shape `(batch_size, height, width, hidden_size)`. Hidden-states of the model at the output of each layer
+ plus the initial embedding outputs reshaped to include the spatial dimensions.
+ """
+
+ loss: torch.FloatTensor | None = None
+ logits: torch.FloatTensor | None = None
+ bool_masked_pos: torch.BoolTensor | None = None
+ ids_restore: torch.LongTensor | None = None
+ hidden_states: tuple[torch.FloatTensor] | None = None
+ attentions: tuple[torch.FloatTensor] | None = None
+ reshaped_hidden_states: tuple[torch.FloatTensor] | None = None
+
+
+class HieraPatchEmbeddings(nn.Module):
+ """
+ This class turns `pixel_values` of shape `(batch_size, num_channels, height, width)` into the initial
+ `hidden_states` (patch embeddings) of shape `(batch_size, seq_length, hidden_size)` to be consumed by a
+ Transformer.
+ """
+
+ def __init__(self, config, is_mae: bool = False):
+ super().__init__()
+
+ # Support any number of spatial dimensions
+ self.spatial_dims = len(config.patch_size)
+ if self.spatial_dims != 2:
+ raise ValueError(f"The number of dimensions of the input image should be 2, but got {self.spatial_dims}.")
+ self.num_channels = config.num_channels
+ self.image_size = config.image_size[-2:]
+ self.tokens_spatial_shape = [i // s for i, s in zip(config.image_size, config.patch_stride)]
+ self.mask_spatial_shape = [i // s for i, s in zip(self.tokens_spatial_shape, config.masked_unit_size)]
+ self.mask_ratio = config.mask_ratio
+ self.is_mae = is_mae
+ self.projection = nn.Conv2d(
+ self.num_channels,
+ config.embed_dim,
+ kernel_size=config.patch_size,
+ stride=config.patch_stride,
+ padding=config.patch_padding,
+ )
+
+ def masked_conv(
+ self, pixel_values: torch.FloatTensor, bool_masked_pos: torch.BoolTensor | None = None
+ ) -> torch.Tensor:
+ """Zero-out the masked regions of the input before conv.
+ Prevents leakage of masked regions when using overlapping kernels.
+ """
+ if bool_masked_pos is None:
+ return self.projection(pixel_values)
+
+ target_size = pixel_values.shape[2:]
+ # Reshape bool_masked_pos to (batch_size, 1, mask_unit_height, mask_unit_width)
+ bool_masked_pos = bool_masked_pos.view(pixel_values.shape[0], 1, *self.mask_spatial_shape)
+
+ bool_masked_pos = nn.functional.interpolate(bool_masked_pos.float(), size=target_size)
+
+ return self.projection(pixel_values * bool_masked_pos)
+
+ def random_masking(
+ self, pixel_values: torch.FloatTensor, noise: torch.FloatTensor | None = None
+ ) -> tuple[torch.BoolTensor, torch.LongTensor]:
+ """
+ Perform per-sample random masking by per-sample shuffling. Per-sample shuffling is done by argsort random
+ noise.
+
+ Args:
+ pixel_values (`torch.FloatTensor` of shape `(batch_size, num_channels, height, width)`)
+ noise (`torch.FloatTensor` of shape `(batch_size, num_mask_units)`, *optional*) which is
+ mainly used for testing purposes to control randomness and maintain the reproducibility
+ """
+ batch_size = pixel_values.shape[0]
+ # Tokens selected for masking at mask unit level
+ num_windows = math.prod(self.mask_spatial_shape)
+ len_keep = int(num_windows * (1 - self.mask_ratio))
+
+ if noise is None:
+ noise = torch.rand(batch_size, num_windows, device=pixel_values.device)
+
+ # Sort noise for each sample
+ ids_shuffle = torch.argsort(noise, dim=1)
+ # ascend: small is keep, large is remove
+ ids_restore = torch.argsort(ids_shuffle, dim=1).to(pixel_values.device)
+
+ # Generate the binary bool_masked_pos: 1 is *keep*, 0 is *remove*
+ # Note this is opposite to original MAE
+ bool_masked_pos = torch.zeros([batch_size, num_windows], device=pixel_values.device)
+ bool_masked_pos[:, :len_keep] = 1
+ # Unshuffle to get the binary bool_masked_pos
+ bool_masked_pos = torch.gather(bool_masked_pos, dim=1, index=ids_restore).bool()
+
+ return bool_masked_pos, ids_restore
+
+ def forward(
+ self,
+ pixel_values: torch.FloatTensor,
+ noise: torch.FloatTensor | None = None,
+ ) -> tuple[torch.Tensor, torch.BoolTensor | None, torch.LongTensor | None]:
+ (bool_masked_pos, ids_restore) = (
+ self.random_masking(pixel_values, noise=noise) if self.is_mae else (None, None)
+ )
+
+ embeddings = self.masked_conv(pixel_values, bool_masked_pos)
+ embeddings = embeddings.flatten(2).transpose(2, 1)
+
+ return embeddings, bool_masked_pos, ids_restore
+
+
+class HieraEmbeddings(nn.Module):
+ """
+ Construct position and patch embeddings.
+ """
+
+ def __init__(self, config: HieraConfig, is_mae: bool = False) -> None:
+ super().__init__()
+ self.patch_stride = config.patch_stride
+ tokens_spatial_shape = [i // s for i, s in zip(config.image_size, config.patch_stride)]
+ self.mask_spatial_shape = [i // s for i, s in zip(tokens_spatial_shape, config.masked_unit_size)]
+ self.num_tokens = math.prod(tokens_spatial_shape)
+ self.is_mae = is_mae
+
+ self.patch_embeddings = HieraPatchEmbeddings(config, is_mae=is_mae)
+
+ self.position_embeddings = nn.Parameter(torch.zeros(1, self.num_tokens, config.embed_dim))
+
+ def interpolate_pos_encoding(
+ self, embeddings: torch.Tensor, pos_embeds: torch.Tensor, height: int, width: int
+ ) -> torch.Tensor:
+ """
+ This method allows to interpolate the pre-trained position encodings, to be able to use the model on higher resolution
+ images. This method is also adapted to support torch.jit tracing, no class embeddings, and different patch strides.
+
+ Adapted from:
+ - https://github.com/facebookresearch/dino/blob/de9ee3df6cf39fac952ab558447af1fa1365362a/vision_transformer.py#L174-L194, and
+ - https://github.com/facebookresearch/dinov2/blob/e1277af2ba9496fbadf7aec6eba56e8d882d1e35/dinov2/models/vision_transformer.py#L179-L211
+ """
+
+ num_patches = embeddings.shape[1]
+ num_positions = pos_embeds.shape[1]
+
+ # always interpolate when tracing to ensure the exported model works for dynamic input shapes
+ if not torch.jit.is_tracing() and num_patches == num_positions and height == width:
+ return pos_embeds
+
+ dim = embeddings.shape[-1]
+
+ new_height = height // self.patch_stride[0]
+ new_width = width // self.patch_stride[1]
+
+ sqrt_num_positions = torch_int(num_positions**0.5)
+ pos_embeds = pos_embeds.reshape(1, sqrt_num_positions, sqrt_num_positions, dim)
+ pos_embeds = pos_embeds.permute(0, 3, 1, 2)
+
+ pos_embeds = nn.functional.interpolate(
+ pos_embeds,
+ size=(new_height, new_width),
+ mode="bicubic",
+ align_corners=False,
+ )
+
+ pos_embeds = pos_embeds.permute(0, 2, 3, 1).view(1, -1, dim)
+ return pos_embeds
+
+ def get_position_embedding(
+ self, embeddings: torch.Tensor, height: int, width: int, interpolate_pos_encoding: bool
+ ) -> torch.FloatTensor:
+ return (
+ self.interpolate_pos_encoding(embeddings, self.position_embeddings, height, width)
+ if interpolate_pos_encoding
+ else self.position_embeddings
+ )
+
+ def forward(
+ self,
+ pixel_values: torch.FloatTensor,
+ noise: torch.FloatTensor | None = None,
+ interpolate_pos_encoding: bool = False,
+ ) -> tuple[torch.Tensor, torch.BoolTensor | None, torch.LongTensor | None]:
+ height, width = pixel_values.shape[-2:]
+ embeddings, bool_masked_pos, ids_restore = self.patch_embeddings(pixel_values, noise=noise)
+ embeddings = embeddings + self.get_position_embedding(embeddings, height, width, interpolate_pos_encoding)
+ return embeddings, bool_masked_pos, ids_restore
+
+
+class HieraMaskUnitAttention(nn.Module):
+ """
+ Computes either Mask Unit or Global Attention. Also is able to perform query pooling.
+
+ Note: this assumes the tokens have already been flattened and unrolled into mask units.
+ """
+
+ def __init__(
+ self,
+ hidden_size: int,
+ hidden_size_output: int,
+ num_heads: int,
+ query_stride: int = 1,
+ window_size: int = 0,
+ use_mask_unit_attn: bool = False,
+ ) -> None:
+ super().__init__()
+ self.num_heads = num_heads
+ self.query_stride = query_stride
+ self.hidden_size_output = hidden_size_output
+
+ self.head_dim = hidden_size_output // num_heads
+ self.scale = (self.head_dim) ** -0.5
+
+ self.qkv = nn.Linear(hidden_size, 3 * hidden_size_output)
+ self.proj = nn.Linear(hidden_size_output, hidden_size_output)
+
+ self.window_size = window_size
+ self.use_mask_unit_attn = use_mask_unit_attn
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ output_attentions: bool = False,
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
+ """Input should be of shape [batch, tokens, channels]."""
+ batch_size, seq_len, _ = hidden_states.shape
+
+ num_windows = 1
+ if self.use_mask_unit_attn:
+ num_windows = seq_len // (self.query_stride * self.window_size)
+
+ qkv = self.qkv(hidden_states)
+ qkv = qkv.reshape(batch_size, -1, num_windows, 3, self.num_heads, self.head_dim)
+ qkv = qkv.permute(3, 0, 4, 2, 1, 5)
+
+ query, key, value = qkv.unbind(0)
+
+ if self.query_stride > 1:
+ # Refer to unroll to see how this performs a maxpool-Nd
+ query = query.view(batch_size, self.num_heads, num_windows, self.query_stride, -1, self.head_dim)
+ query = query.max(dim=3).values
+
+ attn_weights = (query * self.scale) @ key.transpose(-1, -2)
+ attn_weights = attn_weights.softmax(dim=-1)
+
+ attn_output = attn_weights @ value
+ attn_output = attn_output.transpose(1, 3).reshape(batch_size, -1, self.hidden_size_output)
+ attn_output = self.proj(attn_output)
+
+ return (attn_output, attn_weights) if output_attentions else (attn_output, None)
+
+
+# Copied from transformers.models.beit.modeling_beit.drop_path
+def drop_path(input: torch.Tensor, drop_prob: float = 0.0, training: bool = False) -> torch.Tensor:
+ """
+ Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).
+
+ """
+ if drop_prob == 0.0 or not training:
+ return input
+ keep_prob = 1 - drop_prob
+ shape = (input.shape[0],) + (1,) * (input.ndim - 1) # work with diff dim tensors, not just 2D ConvNets
+ random_tensor = keep_prob + torch.rand(shape, dtype=input.dtype, device=input.device)
+ random_tensor.floor_() # binarize
+ output = input.div(keep_prob) * random_tensor
+ return output
+
+
+# Copied from transformers.models.beit.modeling_beit.BeitDropPath with Beit->Hiera
+class HieraDropPath(nn.Module):
+ """Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks)."""
+
+ def __init__(self, drop_prob: float | None = None) -> None:
+ super().__init__()
+ self.drop_prob = drop_prob
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ return drop_path(hidden_states, self.drop_prob, self.training)
+
+ def extra_repr(self) -> str:
+ return f"p={self.drop_prob}"
+
+
+class HieraMlp(nn.Module):
+ def __init__(self, config, dim: int) -> None:
+ super().__init__()
+ self.activation_fn = ACT2FN[config.hidden_act]
+ self.fc1 = nn.Linear(dim, int(dim * config.mlp_ratio))
+ self.fc2 = nn.Linear(int(dim * config.mlp_ratio), dim)
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ hidden_states = self.fc1(hidden_states)
+ hidden_states = self.activation_fn(hidden_states)
+ hidden_states = self.fc2(hidden_states)
+ return hidden_states
+
+
+class HieraLayer(nn.Module):
+ def __init__(
+ self,
+ config,
+ hidden_size: int,
+ hidden_size_output: int,
+ num_heads: int,
+ drop_path: float = 0.0,
+ query_stride: int = 1,
+ window_size: int = 0,
+ use_mask_unit_attn: bool = False,
+ ) -> None:
+ super().__init__()
+
+ self.hidden_size = hidden_size
+ self.hidden_size_output = hidden_size_output
+ self.query_stride = query_stride
+
+ self.layernorm_before = nn.LayerNorm(hidden_size, eps=config.layer_norm_eps)
+ self.attn = HieraMaskUnitAttention(
+ hidden_size=hidden_size,
+ hidden_size_output=hidden_size_output,
+ num_heads=num_heads,
+ query_stride=query_stride,
+ window_size=window_size,
+ use_mask_unit_attn=use_mask_unit_attn,
+ )
+
+ self.layernorm_after = nn.LayerNorm(hidden_size_output, eps=config.layer_norm_eps)
+ self.mlp = HieraMlp(config, hidden_size_output)
+
+ self.drop_path = HieraDropPath(drop_path) if drop_path > 0 else nn.Identity()
+ if hidden_size != hidden_size_output:
+ self.proj = nn.Linear(hidden_size, hidden_size_output)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ output_attentions: bool = False,
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
+ batch_size, seq_len, _ = hidden_states.shape
+ # Attention + Q Pooling
+ hidden_states_norm = self.layernorm_before(hidden_states)
+ if self.hidden_size != self.hidden_size_output:
+ hidden_states = self.proj(hidden_states_norm)
+ # Refer to unroll to see how this performs a maxpool-Nd
+ hidden_states = (
+ hidden_states.view(batch_size, self.query_stride, -1, self.hidden_size_output).max(dim=1).values
+ )
+
+ (hidden_states_norm, attn_weights) = self.attn(hidden_states_norm, output_attentions=output_attentions)
+ hidden_states = hidden_states + self.drop_path(hidden_states_norm)
+
+ residual = hidden_states
+ hidden_states = self.layernorm_after(hidden_states)
+ hidden_states = self.mlp(hidden_states)
+ hidden_states = residual + self.drop_path(hidden_states)
+
+ return (hidden_states, attn_weights)
+
+
+class HieraStage(GradientCheckpointingLayer):
+ def __init__(
+ self,
+ config,
+ depth: int,
+ hidden_size: int,
+ hidden_size_output: int,
+ num_heads: int,
+ drop_path: list[float],
+ query_stride: list[int],
+ window_size: int,
+ use_mask_unit_attn: bool,
+ stage_num: int | None = None,
+ ) -> None:
+ super().__init__()
+ # we need to know if the previous stage used masked attention
+ # mask unit or global attention.
+ # lag by 1 layer, so that global attention,
+ # applied post pooling on lower resolution
+ previous_stage_used_masked_attention = False
+ if stage_num is not None:
+ previous_stage_used_masked_attention = config.masked_unit_attention[stage_num - 1 if stage_num > 0 else 0]
+ self.layers = nn.ModuleList(
+ [
+ HieraLayer(
+ config=config,
+ hidden_size=hidden_size if i == 0 else hidden_size_output,
+ hidden_size_output=hidden_size_output,
+ num_heads=num_heads,
+ drop_path=drop_path[i],
+ query_stride=query_stride[i],
+ window_size=window_size,
+ use_mask_unit_attn=use_mask_unit_attn or (previous_stage_used_masked_attention and i == 0),
+ )
+ for i in range(depth)
+ ]
+ )
+
+ def forward(
+ self, hidden_states: torch.Tensor, output_attentions: bool = False
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
+ for i, layer_module in enumerate(self.layers):
+ (hidden_states, attn_weights) = layer_module(hidden_states, output_attentions=output_attentions)
+
+ return hidden_states, attn_weights
+
+
+def undo_windowing(hidden_states: torch.Tensor, shape: list[int], mask_unit_shape: list[int]) -> torch.Tensor:
+ """
+ Restore spatial organization by undoing windowed organization of mask units.
+
+ Args:
+ hidden_states (`torch.Tensor`): The hidden states tensor of shape `[batch_size, num_mask_unit_height*num_mask_unit_width, hidden_size]`.
+ shape (`list[int]`): The original shape of the hidden states tensor before windowing.
+ mask_unit_shape (`list[int]`): The shape of the mask units used for windowing.
+
+ Returns:
+ torch.Tensor: The restored hidden states tensor of shape [batch_size, num_mask_unit_height*mask_unit_height, num_mask_unit_width*mask_unit_width, hidden_size].
+ """
+ batch_size, hidden_size = hidden_states.shape[0], hidden_states.shape[-1]
+ # From: [batch_size, num_mask_unit_height*num_mask_unit_width, hidden_size]
+ # To: [batch_size, num_mask_unit_height, num_mask_unit_width, mask_unit_height, mask_unit_width, hidden_size]
+ num_mask_units = [s // mu for s, mu in zip(shape, mask_unit_shape)]
+ hidden_states = hidden_states.view(batch_size, *num_mask_units, *mask_unit_shape, hidden_size)
+
+ # From: [batch_size, num_mask_unit_height, num_mask_unit_width, mask_unit_height, mask_unit_width, hidden_size]
+ # To: [batch_size, num_mask_unit_height*mask_unit_height, num_mask_unit_width*mask_unit_width, hidden_size]
+ hidden_states = hidden_states.permute(0, 1, 3, 2, 4, 5)
+ hidden_states = hidden_states.reshape(batch_size, *shape, hidden_size)
+
+ return hidden_states
+
+
+class HieraEncoder(nn.Module):
+ def __init__(self, config: HieraConfig) -> None:
+ super().__init__()
+ total_depth = sum(config.depths)
+ # stochastic depth decay rule
+ dpr = [x.item() for x in torch.linspace(0, config.drop_path_rate, total_depth, device="cpu")]
+ # query strides rule
+ cumulative_depths = torch.tensor(config.depths, device="cpu").cumsum(0).tolist()
+ query_pool_layer = cumulative_depths[: config.num_query_pool]
+ query_strides = [math.prod(config.query_stride) if i in query_pool_layer else 1 for i in range(total_depth)]
+
+ # Transformer blocks
+ self.stages = nn.ModuleList()
+ hidden_size = config.embed_dim
+ stage_ends = [0] + cumulative_depths
+ masked_unit_area = math.prod(config.masked_unit_size)
+ query_stride_area = math.prod(config.query_stride)
+ for idx_stage, depth in enumerate(config.depths):
+ hidden_size_output = int(config.embed_dim * config.embed_dim_multiplier**idx_stage)
+
+ stage = HieraStage(
+ config=config,
+ depth=depth,
+ hidden_size=hidden_size,
+ hidden_size_output=hidden_size_output,
+ num_heads=config.num_heads[idx_stage],
+ drop_path=dpr[stage_ends[idx_stage] : stage_ends[idx_stage + 1]],
+ query_stride=query_strides[stage_ends[idx_stage] : stage_ends[idx_stage + 1]],
+ window_size=int(masked_unit_area * query_stride_area**-idx_stage),
+ use_mask_unit_attn=config.masked_unit_attention[idx_stage],
+ stage_num=idx_stage,
+ )
+
+ hidden_size = hidden_size_output
+ self.stages.append(stage)
+
+ # Setting reroll schedule
+ # The first stage has to reverse everything
+ # The next stage has to reverse all but the first unroll, etc.
+ stage_size = [i // s for i, s in zip(config.image_size, config.patch_stride)]
+ unroll_schedule = [config.query_stride] * len(config.depths[:-1])
+
+ self.schedule = {}
+ for idx_stage in range(len(config.depths)):
+ self.schedule[idx_stage] = unroll_schedule, stage_size
+ if idx_stage < config.num_query_pool:
+ stage_size = [i // s for i, s in zip(stage_size, config.query_stride)]
+ unroll_schedule = unroll_schedule[1:]
+
+ self.gradient_checkpointing = False
+
+ def reroll(
+ self, hidden_states: torch.Tensor, stage_idx: int, bool_masked_pos: torch.BoolTensor | None = None
+ ) -> torch.Tensor:
+ """
+ Roll the given tensor back up to spatial order assuming it's from the given block.
+
+ If no bool_masked_pos is provided returns:
+ - [batch_size, height, width, hidden_size]
+ If a bool_masked_pos is provided returns:
+ - [batch_size, num_mask_units, mask_unit_height, mask_unit_width, hidden_size]
+ """
+ schedule, size = self.schedule[stage_idx]
+ batch_size, seq_len, hidden_size = hidden_states.shape
+
+ num_dim = len(size)
+ mask_unit_shape = [1] * num_dim
+
+ for strides in schedule:
+ # Extract the current patch from seq_len
+ hidden_states = hidden_states.view(
+ batch_size, *strides, seq_len // math.prod(strides), *mask_unit_shape, hidden_size
+ )
+
+ # Move that patch into the current MU
+ # Input: [batch_size, stride, stride, seq_len//(stride*stride), mask_unit_height, mask_unit_width, hidden_size]
+ # Output: [batch_size, seq_len//(stride*stride), stride, mask_unit_height, stride, mask_unit_width, hidden_size]
+ hidden_states = hidden_states.permute(0, 3, 1, 4, 2, 5, 6)
+
+ # Reshape to [batch_size, seq_len//(stride*stride), *mask_units, hidden_size]
+ for i in range(num_dim):
+ mask_unit_shape[i] *= strides[i]
+ hidden_states = hidden_states.reshape(batch_size, -1, *mask_unit_shape, hidden_size)
+ seq_len = hidden_states.shape[1]
+
+ # Current shape (e.g., 2d: [batch_size, #num_mask_units_height*#num_mask_units_width, mask_unit_height, mask_unit_width, hidden_size])
+ hidden_states = hidden_states.view(batch_size, seq_len, *mask_unit_shape, hidden_size)
+
+ # If masked, return [batch_size, num_mask_units, mask_unit_height, mask_unit_width, hidden_size]
+ if bool_masked_pos is not None:
+ return hidden_states
+
+ # If not masked, we can return [batch_size, height, width, hidden_size]
+ hidden_states = undo_windowing(hidden_states, size, mask_unit_shape)
+
+ return hidden_states
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ bool_masked_pos: torch.BoolTensor | None = None,
+ output_attentions: bool = False,
+ output_hidden_states: bool = False,
+ return_dict: bool = True,
+ ) -> tuple | BaseModelOutput:
+ all_hidden_states = () if output_hidden_states else None
+ all_reshaped_hidden_states = () if output_hidden_states else None
+ all_self_attentions = () if output_attentions else None
+
+ if output_hidden_states:
+ all_hidden_states = all_hidden_states + (hidden_states,)
+ reshaped_hidden_states = self.reroll(hidden_states, stage_idx=0, bool_masked_pos=bool_masked_pos)
+ all_reshaped_hidden_states = all_reshaped_hidden_states + (reshaped_hidden_states,)
+
+ for i, stage_module in enumerate(self.stages):
+ layer_outputs = stage_module(hidden_states, output_attentions)
+
+ hidden_states = layer_outputs[0]
+
+ if output_attentions:
+ all_self_attentions = all_self_attentions + (layer_outputs[1],)
+
+ if output_hidden_states:
+ all_hidden_states = all_hidden_states + (hidden_states,)
+ reshaped_hidden_states = self.reroll(hidden_states, stage_idx=i, bool_masked_pos=bool_masked_pos)
+ all_reshaped_hidden_states = all_reshaped_hidden_states + (reshaped_hidden_states,)
+
+ if not return_dict:
+ return tuple(
+ v
+ for v in [hidden_states, all_hidden_states, all_self_attentions, all_reshaped_hidden_states]
+ if v is not None
+ )
+ return HieraEncoderOutput(
+ last_hidden_state=hidden_states,
+ hidden_states=all_hidden_states,
+ attentions=all_self_attentions,
+ reshaped_hidden_states=all_reshaped_hidden_states,
+ )
+
+
+def unroll(
+ hidden_states: torch.Tensor, image_shape: tuple[int, int], patch_stride: tuple[int, int], schedule: list[list[int]]
+) -> torch.Tensor:
+ """
+ Reorders the tokens such that patches are contiguous in memory.
+ E.g., given [batch_size, (height, width), hidden_size] and stride of (stride, stride), this will re-order the tokens as
+ [batch_size, (stride, stride, height // stride, width // stride), hidden_size]
+
+ This allows operations like Max2d to be computed as x.view(batch_size, stride*stride, -1, hidden_size).max(dim=1).
+ Not only is this faster, but it also makes it easy to support inputs of arbitrary
+ dimensions in addition to patch-wise sparsity.
+
+ Performing this operation multiple times in sequence puts entire windows as contiguous
+ in memory. For instance, if you applied the stride (2, 2) 3 times, entire windows of
+ size 8x8 would be contiguous in memory, allowing operations like mask unit attention
+ computed easily and efficiently, while also allowing max to be applied sequentially.
+
+ Note: This means that intermediate values of the model are not in height x width order, so they
+ need to be re-rolled if you want to use the intermediate values as a height x width feature map.
+ The last block of the network is fine though, since by then the strides are all consumed.
+ """
+ batch_size, _, hidden_size = hidden_states.shape
+
+ size = [i // s for i, s in zip(image_shape, patch_stride)]
+
+ current_size = size
+ hidden_states = hidden_states.view(*([batch_size] + current_size + [hidden_size]))
+
+ for strides in schedule:
+ # Move patches with the given strides to the batch dimension
+
+ # Create a view of the tensor with the patch stride as separate dims
+ # For example in 2d: [batch_size, height // stride, stride, width // stride, stride, C]
+ current_size = [i // s for i, s in zip(current_size, strides)]
+ # initialize new_shape with [height // stride, stride, width // stride, stride]
+ new_shape = [item for pair in zip(current_size, strides) for item in pair]
+ # add batch_size and hidden_size to new_shape
+ new_shape = [batch_size] + new_shape + [hidden_size]
+ hidden_states = hidden_states.view(new_shape)
+
+ # Move the patch stride into the batch dimension
+ # For example in 2d: [batch_size, stride, stride, height // stride, width // stride, hidden_size]
+ num_dims = len(new_shape)
+ permute = [0] + list(range(2, num_dims - 1, 2)) + list(range(1, num_dims - 1, 2)) + [num_dims - 1]
+ hidden_states = hidden_states.permute(permute)
+
+ # Now finally flatten the relevant dims into the batch dimension
+ hidden_states = hidden_states.flatten(0, len(strides))
+ batch_size *= math.prod(strides)
+
+ hidden_states = hidden_states.reshape(-1, math.prod(size), hidden_size)
+ return hidden_states
+
+
+@auto_docstring
+class HieraPreTrainedModel(PreTrainedModel):
+ config: HieraConfig
+ base_model_prefix = "hiera"
+ main_input_name = "pixel_values"
+ input_modalities = ("image",)
+ supports_gradient_checkpointing = True
+
+ @torch.no_grad()
+ def _init_weights(self, module) -> None:
+ """Initialize the weights"""
+ std = self.config.initializer_range
+
+ if isinstance(module, HieraEmbeddings):
+ init.trunc_normal_(module.position_embeddings, std=std)
+
+ elif isinstance(module, HieraDecoder):
+ init.trunc_normal_(module.mask_token, std=std)
+ init.trunc_normal_(module.decoder_position_embeddings, std=std)
+
+ elif isinstance(module, (nn.Linear, nn.Conv1d, nn.Conv2d)):
+ init.trunc_normal_(module.weight, std=std)
+ if module.bias is not None:
+ init.constant_(module.bias, std)
+
+ elif isinstance(module, nn.LayerNorm):
+ init.constant_(module.bias, std)
+ init.constant_(module.weight, self.config.layer_norm_init)
+
+
+class HieraPooler(nn.Module):
+ def __init__(self, config: HieraConfig):
+ super().__init__()
+ num_features = int(config.embed_dim * config.embed_dim_multiplier ** (len(config.depths) - 1))
+ self.layernorm = nn.LayerNorm(num_features, eps=config.layer_norm_eps)
+ self.pooler = nn.AdaptiveAvgPool1d(1)
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ hidden_states = hidden_states.transpose(1, 2)
+ pooled_output = self.pooler(hidden_states)
+ pooled_output = torch.flatten(pooled_output, 1)
+ pooled_output = self.layernorm(pooled_output)
+ return pooled_output
+
+
+@auto_docstring
+class HieraModel(HieraPreTrainedModel):
+ def __init__(self, config: HieraConfig, add_pooling_layer: bool = True, is_mae: bool = False):
+ r"""
+ add_pooling_layer (`bool`, *optional*, defaults to `True`):
+ Whether or not to apply pooling layer.
+ is_mae (`bool`, *optional*, defaults to `False`):
+ Whether or not to run the model on MAE mode.
+ """
+ super().__init__(config)
+ self.num_features = int(config.embed_dim * config.embed_dim_multiplier ** (len(config.depths) - 1))
+
+ self.embeddings = HieraEmbeddings(config, is_mae=is_mae)
+ self.encoder = HieraEncoder(config)
+
+ self.unroll_schedule = [config.query_stride] * len(config.depths[:-1])
+
+ self.pooler = HieraPooler(config) if add_pooling_layer else None
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ def get_input_embeddings(self) -> HieraPatchEmbeddings:
+ return self.embeddings.patch_embeddings
+
+ @auto_docstring
+ def forward(
+ self,
+ pixel_values: torch.Tensor | None = None,
+ noise: torch.FloatTensor | None = None,
+ output_attentions: bool | None = None,
+ output_hidden_states: bool | None = None,
+ interpolate_pos_encoding: bool | None = None,
+ return_dict: bool | None = None,
+ **kwargs,
+ ) -> tuple | BaseModelOutputWithPooling:
+ r"""
+ noise (`torch.FloatTensor` of shape `(batch_size, num_mask_units)`, *optional*):
+ Mainly used for testing purposes to control randomness and maintain the reproducibility
+ """
+ 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.return_dict
+
+ if pixel_values is None:
+ raise ValueError("You have to specify pixel_values")
+
+ embedding_output, bool_masked_pos, ids_restore = self.embeddings(
+ pixel_values, interpolate_pos_encoding=interpolate_pos_encoding, noise=noise
+ )
+
+ image_shape = (pixel_values.shape[-2], pixel_values.shape[-1])
+ hidden_states = unroll(
+ embedding_output,
+ image_shape=image_shape,
+ patch_stride=self.config.patch_stride,
+ schedule=self.unroll_schedule,
+ )
+
+ # Discard masked tokens if bool_masked_pos is provided
+ if bool_masked_pos is not None:
+ mask_unit_area = math.prod(self.config.masked_unit_size)
+ batch_size, _, hidden_size = hidden_states.shape
+ positions = bool_masked_pos.unsqueeze(-1).tile(1, mask_unit_area, hidden_size)
+ hidden_states = hidden_states[positions]
+ hidden_states = hidden_states.view(batch_size, -1, hidden_size)
+
+ encoder_outputs = self.encoder(
+ hidden_states,
+ bool_masked_pos=bool_masked_pos,
+ output_attentions=output_attentions,
+ output_hidden_states=output_hidden_states,
+ return_dict=return_dict,
+ )
+ sequence_output = encoder_outputs[0]
+ pooled_output = None
+ if self.pooler is not None:
+ pooled_output = self.pooler(sequence_output)
+
+ if not return_dict:
+ head_outputs = (sequence_output, pooled_output) if pooled_output is not None else (sequence_output,)
+ head_outputs = (
+ head_outputs + (bool_masked_pos, ids_restore) if bool_masked_pos is not None else head_outputs
+ )
+ return head_outputs + encoder_outputs[1:]
+
+ return HieraModelOutput(
+ last_hidden_state=sequence_output,
+ pooler_output=pooled_output,
+ bool_masked_pos=bool_masked_pos,
+ ids_restore=ids_restore,
+ hidden_states=encoder_outputs.hidden_states,
+ attentions=encoder_outputs.attentions,
+ reshaped_hidden_states=encoder_outputs.reshaped_hidden_states,
+ )
+
+
+class HieraDecoder(nn.Module):
+ def __init__(self, config: HieraConfig):
+ super().__init__()
+ num_features = int(config.embed_dim * config.embed_dim_multiplier ** (len(config.depths) - 1))
+ tokens_spatial_shape = [i // s for i, s in zip(config.image_size, config.patch_stride)]
+ self.tokens_spatial_shape_final = [
+ i // s ** (config.num_query_pool) for i, s in zip(tokens_spatial_shape, config.query_stride)
+ ]
+ self.mask_unit_spatial_shape_final = [
+ i // s ** (config.num_query_pool) for i, s in zip(config.masked_unit_size, config.query_stride)
+ ]
+
+ self.decoder_embeddings = nn.Linear(num_features, config.decoder_hidden_size)
+
+ self.mask_token = nn.Parameter(torch.zeros(1, 1, config.decoder_hidden_size))
+
+ self.decoder_position_embeddings = nn.Parameter(
+ torch.zeros(1, math.prod(self.tokens_spatial_shape_final), config.decoder_hidden_size)
+ )
+
+ self.decoder_block = HieraStage(
+ config=config,
+ hidden_size=config.decoder_hidden_size,
+ hidden_size_output=config.decoder_hidden_size,
+ num_heads=config.decoder_num_heads,
+ depth=config.decoder_depth,
+ use_mask_unit_attn=False,
+ drop_path=[0.0] * config.decoder_depth,
+ query_stride=[1] * config.decoder_depth,
+ window_size=0,
+ )
+
+ self.decoder_norm = nn.LayerNorm(config.decoder_hidden_size, eps=config.layer_norm_eps)
+
+ # patch stride of prediction
+ self.pred_stride = config.patch_stride[-1] * (config.query_stride[-1] ** config.num_query_pool)
+ pred_dim = (self.pred_stride ** len(config.query_stride)) * config.num_channels
+
+ self.decoder_pred = nn.Linear(config.decoder_hidden_size, pred_dim)
+
+ def forward(
+ self,
+ encoder_hidden_states: torch.Tensor,
+ bool_masked_pos: torch.BoolTensor,
+ output_attentions: bool = False,
+ ) -> tuple[torch.Tensor, torch.BoolTensor]:
+ # Embed tokens
+ hidden_states = self.decoder_embeddings(encoder_hidden_states)
+
+ # Combine visible and bool_masked_pos tokens
+
+ # hidden_states : [batch_size, num_mask_units_visible, *mask_unit_spatial_shape_final, decoder_hidden_size]
+ # bool_masked_pos: [batch_size, num_mask_units]
+ mask_unit_height, mask_unit_width, decoder_hidden_size = hidden_states.shape[2:]
+ batch_size, num_mask_units = bool_masked_pos.shape
+
+ decoder_hidden_states = torch.zeros(
+ batch_size,
+ num_mask_units,
+ mask_unit_height,
+ mask_unit_width,
+ decoder_hidden_size,
+ device=hidden_states.device,
+ dtype=hidden_states.dtype,
+ )
+ mask_tokens = self.mask_token.view(1, 1, 1, 1, -1)
+ bool_masked_pos = bool_masked_pos.reshape(batch_size, num_mask_units, 1, 1, 1)
+ bool_masked_pos = bool_masked_pos.expand(-1, -1, mask_unit_height, mask_unit_width, decoder_hidden_size)
+ decoder_hidden_states[bool_masked_pos] = hidden_states.flatten()
+ decoder_hidden_states = (
+ 1 - bool_masked_pos.float()
+ ) * mask_tokens + bool_masked_pos.float() * decoder_hidden_states
+
+ # Get back spatial order
+ hidden_states = undo_windowing(
+ decoder_hidden_states,
+ self.tokens_spatial_shape_final,
+ self.mask_unit_spatial_shape_final,
+ )
+ bool_masked_pos = undo_windowing(
+ bool_masked_pos[..., 0:1],
+ self.tokens_spatial_shape_final,
+ self.mask_unit_spatial_shape_final,
+ )
+
+ # Flatten
+ hidden_states = hidden_states.reshape(hidden_states.shape[0], -1, hidden_states.shape[-1])
+ bool_masked_pos = bool_masked_pos.view(hidden_states.shape[0], -1)
+
+ # Add pos embed
+ hidden_states = hidden_states + self.decoder_position_embeddings
+
+ # Apply decoder blocks
+ hidden_states, attn_weights = self.decoder_block(hidden_states, output_attentions=output_attentions)
+ hidden_states = self.decoder_norm(hidden_states)
+
+ # Predictor projection
+ hidden_states = self.decoder_pred(hidden_states)
+
+ return hidden_states, bool_masked_pos
+
+
+class HieraMultiScaleHead(nn.Module):
+ def __init__(self, config: HieraConfig):
+ super().__init__()
+ self.mask_unit_spatial_shape_final = [
+ i // s ** (config.num_query_pool) for i, s in zip(config.masked_unit_size, config.query_stride)
+ ]
+ self.stage_dimensions = [
+ int(config.embed_dim * config.embed_dim_multiplier**i) for i in range(len(config.depths))
+ ]
+ current_masked_unit_size = config.masked_unit_size
+ self.multi_scale_fusion_heads = nn.ModuleList()
+
+ for idx in range(config.num_query_pool):
+ kernel = [i // s for i, s in zip(current_masked_unit_size, self.mask_unit_spatial_shape_final)]
+ current_masked_unit_size = [i // s for i, s in zip(current_masked_unit_size, config.query_stride)]
+ self.multi_scale_fusion_heads.append(
+ nn.Conv2d(
+ self.stage_dimensions[idx],
+ self.stage_dimensions[-1],
+ kernel_size=kernel,
+ stride=kernel,
+ )
+ )
+ self.multi_scale_fusion_heads.append(nn.Identity())
+
+ def apply_fusion_head(self, head: nn.Module, hidden_states: torch.Tensor) -> torch.Tensor:
+ if isinstance(head, nn.Identity):
+ return hidden_states
+
+ batch_size, num_mask_units, mask_unit_height, mask_unit_width, hidden_size = hidden_states.shape
+ # From: [batch_size, num_mask_units, mask_unit_height, mask_unit_width, hidden_size]
+ # To: head([batch_size * num_mask_units, hidden_size, mask_unit_height, mask_unit_width])
+ hidden_states = hidden_states.reshape(
+ batch_size * num_mask_units, mask_unit_height, mask_unit_width, hidden_size
+ )
+ hidden_states = hidden_states.permute(0, 3, 1, 2)
+ hidden_states = head(hidden_states)
+
+ # Restore original layout
+ hidden_states = hidden_states.permute(0, 2, 3, 1)
+ mask_unit_height_final, mask_unit_width_final, hidden_size = hidden_states.shape[1:]
+ hidden_states = hidden_states.reshape(
+ batch_size, num_mask_units, mask_unit_height_final, mask_unit_width_final, hidden_size
+ )
+
+ return hidden_states
+
+ def forward(self, feature_maps: list[torch.Tensor]) -> torch.Tensor:
+ # Multi-scale fusion
+ hidden_states = 0.0
+ for head, feature_map in zip(self.multi_scale_fusion_heads, feature_maps):
+ hidden_states = hidden_states + self.apply_fusion_head(head, feature_map)
+
+ return hidden_states
+
+
+@auto_docstring(
+ custom_intro="""
+ The Hiera Model transformer with the decoder on top for self-supervised pre-training.
+
+
+
+ Note that we provide a script to pre-train this model on custom data in our [examples
+ directory](https://github.com/huggingface/transformers/tree/main/examples/pytorch/image-pretraining).
+
+
+ """
+)
+class HieraForPreTraining(HieraPreTrainedModel):
+ def __init__(self, config: HieraConfig) -> None:
+ super().__init__(config)
+ # Encoder
+ self.hiera = HieraModel(config, add_pooling_layer=False, is_mae=True)
+ self.encoder_norm = nn.LayerNorm(self.hiera.num_features, eps=config.layer_norm_eps)
+ # Multi-scale fusion heads
+ self.multiscale_fusion = HieraMultiScaleHead(config)
+ # Decoder
+ self.decoder = HieraDecoder(config)
+ self.pred_stride = self.decoder.pred_stride
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ def get_pixel_label_2d(self, pixel_values: torch.Tensor, bool_masked_pos: torch.BoolTensor) -> torch.Tensor:
+ # bool_masked_pos (boolean tensor): True means *masked*
+ pixel_values = pixel_values.permute(0, 2, 3, 1)
+
+ size = self.pred_stride
+ label = pixel_values.unfold(1, size, size).unfold(2, size, size)
+ label = label.flatten(1, 2).flatten(2)
+ label = label[bool_masked_pos]
+ if self.config.normalize_pixel_loss:
+ mean = label.mean(dim=-1, keepdim=True)
+ var = label.var(dim=-1, keepdim=True)
+ label = (label - mean) / (var + 1.0e-6) ** 0.5
+
+ return label
+
+ def forward_loss(self, pixel_values: torch.Tensor, logits: torch.Tensor, bool_masked_pos: torch.BoolTensor):
+ # We invert the bool_masked_pos such that 1.0 is *masked*
+ bool_masked_pos = ~bool_masked_pos
+ label = self.get_pixel_label_2d(pixel_values, bool_masked_pos)
+
+ logits = logits[bool_masked_pos]
+ loss = (logits - label) ** 2
+ loss = loss.mean()
+
+ return loss
+
+ @auto_docstring
+ def forward(
+ self,
+ pixel_values: torch.Tensor | None = None,
+ noise: torch.FloatTensor | None = None,
+ output_attentions: bool | None = None,
+ output_hidden_states: bool | None = None,
+ interpolate_pos_encoding: bool | None = None,
+ return_dict: bool | None = None,
+ **kwargs,
+ ) -> tuple | HieraForPreTrainingOutput:
+ r"""
+ noise (`torch.FloatTensor` of shape `(batch_size, num_mask_units)`, *optional*):
+ Mainly used for testing purposes to control randomness and maintain the reproducibility
+
+ Examples:
+ ```python
+ >>> from transformers import AutoImageProcessor, HieraForPreTraining
+ >>> import torch
+ >>> from PIL import Image
+ >>> import httpx
+ >>> from io import BytesIO
+
+ >>> url = "http://images.cocodataset.org/val2017/000000039769.jpg"
+ >>> with httpx.stream("GET", url) as response:
+ ... image = Image.open(BytesIO(response.read()))
+
+ >>> image_processor = AutoImageProcessor.from_pretrained("facebook/hiera-tiny-224-mae-hf")
+ >>> model = HieraForPreTraining.from_pretrained("facebook/hiera-tiny-224-mae-hf")
+
+ >>> inputs = image_processor(images=image, return_tensors="pt")
+
+ >>> outputs = model(**inputs)
+ >>> logits = outputs.logits
+ >>> loss = outputs.loss
+ >>> print(list(logits.shape))
+ [1, 196, 768]
+ ```"""
+ return_dict = return_dict if return_dict is not None else self.config.return_dict
+ 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
+ )
+
+ outputs = self.hiera(
+ pixel_values,
+ noise=noise,
+ output_attentions=output_attentions,
+ output_hidden_states=True,
+ interpolate_pos_encoding=interpolate_pos_encoding,
+ return_dict=return_dict,
+ )
+
+ feature_maps = outputs[-1]
+ bool_masked_pos = outputs[1]
+ ids_to_restore = outputs[2]
+ # Take only the query pooled and last hidden states
+ feature_maps = feature_maps[1 : self.hiera.config.num_query_pool + 1] + (feature_maps[-1],)
+ fused_hidden_states = self.multiscale_fusion(feature_maps)
+ fused_hidden_states = self.encoder_norm(fused_hidden_states)
+
+ # Reconstruct pixel values
+ logits, bool_masked_pos = self.decoder(
+ fused_hidden_states,
+ bool_masked_pos=bool_masked_pos,
+ output_attentions=output_attentions,
+ )
+
+ loss = self.forward_loss(pixel_values, logits, bool_masked_pos)
+
+ if not return_dict:
+ output = (logits, bool_masked_pos, ids_to_restore)
+ if output_hidden_states:
+ output = output + (outputs[3],)
+ if output_attentions:
+ output = output + (outputs[4],)
+ if output_hidden_states:
+ output = output + (outputs[-1],)
+ return ((loss,) + output) if loss is not None else output
+
+ return HieraForPreTrainingOutput(
+ loss=loss,
+ logits=logits,
+ bool_masked_pos=bool_masked_pos,
+ ids_restore=ids_to_restore,
+ hidden_states=outputs.hidden_states if output_hidden_states else None,
+ attentions=outputs.attentions,
+ reshaped_hidden_states=outputs.reshaped_hidden_states if output_hidden_states else None,
+ )
+
+
+@auto_docstring(
+ custom_intro="""
+ Hiera Model transformer with an image classification head on top (a linear layer on top of the final hidden state with
+ average pooling) e.g. for ImageNet.
+
+
+
+ Note that it's possible to fine-tune Hiera on higher resolution images than the ones it has been trained on, by
+ setting `interpolate_pos_encoding` to `True` in the forward of the model. This will interpolate the pre-trained
+ position embeddings to the higher resolution.
+
+
+ """
+)
+class HieraForImageClassification(HieraPreTrainedModel):
+ def __init__(self, config: HieraConfig) -> None:
+ super().__init__(config)
+
+ self.num_labels = config.num_labels
+ self.hiera = HieraModel(config, add_pooling_layer=True, is_mae=False)
+
+ # Classifier head
+ self.classifier = (
+ nn.Linear(self.hiera.num_features, config.num_labels) if config.num_labels > 0 else nn.Identity()
+ )
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @auto_docstring
+ def forward(
+ self,
+ pixel_values,
+ labels: torch.Tensor | None = None,
+ output_attentions: bool | None = None,
+ output_hidden_states: bool | None = None,
+ interpolate_pos_encoding: bool | None = None,
+ return_dict: bool | None = None,
+ **kwargs,
+ ) -> tuple | HieraForImageClassificationOutput:
+ r"""
+ labels (`torch.LongTensor` of shape `(batch_size,)`, *optional*):
+ Labels for computing the image classification/regression loss. Indices should be in `[0, ...,
+ config.num_labels - 1]`. If `config.num_labels == 1` a regression loss is computed (Mean-Square loss), If
+ `config.num_labels > 1` a classification loss is computed (Cross-Entropy).
+ """
+ return_dict = return_dict if return_dict is not None else self.config.return_dict
+ 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
+ )
+
+ outputs = self.hiera(
+ pixel_values,
+ output_attentions=output_attentions,
+ output_hidden_states=output_hidden_states,
+ interpolate_pos_encoding=interpolate_pos_encoding,
+ return_dict=return_dict,
+ )
+
+ pooled_output = outputs[1]
+
+ logits = self.classifier(pooled_output)
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(labels, logits, self.config)
+
+ if not return_dict:
+ output = (logits,) + outputs[2:]
+ return ((loss,) + output) if loss is not None else output
+
+ return HieraForImageClassificationOutput(
+ loss=loss,
+ logits=logits,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ reshaped_hidden_states=outputs.reshaped_hidden_states,
+ )
+
+
+@auto_docstring(
+ custom_intro="""
+ Hiera backbone, to be used with frameworks like DETR and MaskFormer.
+ """
+)
+class HieraBackbone(BackboneMixin, HieraPreTrainedModel):
+ def __init__(self, config: HieraConfig):
+ super().__init__(config)
+
+ self.num_features = [config.embed_dim] + [
+ int(config.embed_dim * config.embed_dim_multiplier**i) for i in range(len(config.depths))
+ ]
+ self.embeddings = HieraEmbeddings(config, is_mae=False)
+ self.encoder = HieraEncoder(config)
+
+ # Add layer norms to hidden states of out_features
+ hidden_states_norms = {}
+ for stage, num_channels in zip(self.out_features, self.channels):
+ hidden_states_norms[stage] = nn.LayerNorm(num_channels)
+ self.hidden_states_norms = nn.ModuleDict(hidden_states_norms)
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ def get_input_embeddings(self):
+ return self.embeddings.patch_embeddings
+
+ @can_return_tuple
+ @filter_output_hidden_states
+ def forward(
+ self,
+ pixel_values: torch.Tensor,
+ output_hidden_states: bool | None = None,
+ output_attentions: bool | None = None,
+ return_dict: bool | None = None,
+ **kwargs,
+ ) -> BackboneOutput:
+ """
+ Returns:
+
+ Examples:
+
+ ```python
+ >>> from transformers import AutoImageProcessor, AutoBackbone
+ >>> import torch
+ >>> from PIL import Image
+ >>> import httpx
+ >>> from io import BytesIO
+
+ >>> url = "http://images.cocodataset.org/val2017/000000039769.jpg"
+ >>> with httpx.stream("GET", url) as response:
+ ... image = Image.open(BytesIO(response.read()))
+
+ >>> processor = AutoImageProcessor.from_pretrained("facebook/hiera-tiny-224-hf")
+ >>> model = AutoBackbone.from_pretrained(
+ ... "facebook/hiera-tiny-224-hf", out_features=["stage1", "stage2", "stage3", "stage4"]
+ ... )
+
+ >>> inputs = processor(image, return_tensors="pt")
+ >>> outputs = model(**inputs)
+ >>> feature_maps = outputs.feature_maps
+ >>> list(feature_maps[-1].shape)
+ [1, 768, 7, 7]
+ ```"""
+ return_dict = return_dict if return_dict is not None else self.config.return_dict
+ output_hidden_states = (
+ output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
+ )
+ output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
+
+ embedding_output, _, _ = self.embeddings(pixel_values)
+
+ outputs = self.encoder(
+ embedding_output,
+ output_attentions=output_attentions,
+ output_hidden_states=True,
+ return_dict=return_dict,
+ )
+
+ hidden_states = outputs[-1]
+
+ feature_maps = ()
+ for stage, hidden_state in zip(self.stage_names, hidden_states):
+ if stage in self.out_features:
+ batch_size, height, width, num_channels = hidden_state.shape
+ hidden_state = hidden_state.view(batch_size, height * width, num_channels)
+ hidden_state = self.hidden_states_norms[stage](hidden_state)
+ hidden_state = hidden_state.view(batch_size, height, width, num_channels)
+ hidden_state = hidden_state.permute(0, 3, 1, 2).contiguous()
+ feature_maps += (hidden_state,)
+
+ if not return_dict:
+ output = (feature_maps,)
+ if output_hidden_states:
+ output += (outputs[1],)
+ if output_attentions:
+ output += (outputs[2],)
+ return output
+
+ return BackboneOutput(
+ feature_maps=feature_maps,
+ hidden_states=outputs[1] if output_hidden_states else None,
+ attentions=outputs[2] if output_attentions else None,
+ )
+
+
+__all__ = ["HieraForImageClassification", "HieraForPreTraining", "HieraBackbone", "HieraModel", "HieraPreTrainedModel"]
diff --git a/third_party/transformers/src/transformers/models/higgs_audio_v2/__init__.py b/third_party/transformers/src/transformers/models/higgs_audio_v2/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e2fbab15b6652e7105721b1ff51bf88aea06af5b
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/higgs_audio_v2/__init__.py
@@ -0,0 +1,29 @@
+# Copyright 2025 Boson AI and The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_higgs_audio_v2 import *
+ from .generation_higgs_audio_v2 import *
+ from .modeling_higgs_audio_v2 import *
+ from .processing_higgs_audio_v2 import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/higgs_audio_v2/configuration_higgs_audio_v2.py b/third_party/transformers/src/transformers/models/higgs_audio_v2/configuration_higgs_audio_v2.py
new file mode 100644
index 0000000000000000000000000000000000000000..97823eb79576e1abf0762149bdddc3436b25070b
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/higgs_audio_v2/configuration_higgs_audio_v2.py
@@ -0,0 +1,131 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/higgs_audio_v2/modular_higgs_audio_v2.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_higgs_audio_v2.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2025 the HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...modeling_rope_utils import RopeParameters
+from ...utils import auto_docstring
+from ...utils.type_validators import interval
+
+
+@auto_docstring(checkpoint="bosonai/higgs-audio-v2-generation-3B-base")
+@strict
+class HiggsAudioV2Config(PreTrainedConfig):
+ r"""
+ audio_bos_token_id (`int`, *optional*, defaults to 128013):
+ The token ID for the beginning-of-sequence token for audio output.
+ audio_delay_token_id (`int`, *optional*, defaults to 128014):
+ The token ID used for audio delay pattern in multi-codebook generation.
+ audio_stream_bos_id (`int`, *optional*, defaults to 1024):
+ The ID for the beginning-of-stream token in audio sequences.
+ audio_stream_eos_id (`int`, *optional*, defaults to 1025):
+ The ID for the end-of-stream token in audio sequences.
+
+ Example:
+
+ ```python
+ >>> from transformers import HiggsAudioV2Model, HiggsAudioV2Config
+
+ >>> # Initializing a HiggsAudioV2 style configuration
+ >>> configuration = HiggsAudioV2Config()
+
+ >>> # Initializing a model from the configuration
+ >>> model = HiggsAudioV2Model(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "higgs_audio_v2"
+ keys_to_ignore_at_inference = ["past_key_values"]
+ # Default tensor parallel plan for base model `HiggsAudioV2Model`
+ base_model_tp_plan = {
+ "layers.*.self_attn.q_proj": "colwise",
+ "layers.*.self_attn.k_proj": "colwise",
+ "layers.*.self_attn.v_proj": "colwise",
+ "layers.*.self_attn.o_proj": "rowwise",
+ "layers.*.mlp.gate_proj": "colwise",
+ "layers.*.mlp.up_proj": "colwise",
+ "layers.*.mlp.down_proj": "rowwise",
+ }
+ base_model_pp_plan = {
+ "embed_tokens": (["input_ids"], ["inputs_embeds"]),
+ "layers": (["hidden_states", "attention_mask"], ["hidden_states"]),
+ "norm": (["hidden_states"], ["hidden_states"]),
+ }
+
+ vocab_size: int = 128256
+ hidden_size: int = 3072
+ intermediate_size: int = 8192
+ num_hidden_layers: int = 28
+ num_attention_heads: int = 24
+ num_key_value_heads: int = 8
+ hidden_act: str = "silu"
+ max_position_embeddings: int = 2048
+ initializer_range: float = interval(min=0.0, max=1.0)(default=0.02)
+ rms_norm_eps: float = 1e-5
+ use_cache: bool = True
+ pad_token_id: int | None = 128001
+ bos_token_id: int | None = 1
+ eos_token_id: int | list[int] | None = 128009
+ pretraining_tp: int | None = 1
+ tie_word_embeddings: bool = False
+ rope_parameters: RopeParameters | dict | None = None
+ attention_bias: bool = False
+ attention_dropout: int | float | None = 0.0
+ mlp_bias: bool = False
+ head_dim: int | None = 128
+ num_codebooks: int = 8
+ codebook_size: int = 1024
+ audio_token_id: int = 128016
+ audio_bos_token_id: int = 128013
+ audio_delay_token_id: int = 128014
+ audio_stream_bos_id: int = 1024
+ audio_stream_eos_id: int = 1025
+
+ def __post_init__(self, **kwargs):
+ if self.rope_parameters is None:
+ self.rope_parameters = {
+ "factor": 32.0,
+ "rope_theta": 500000.0,
+ "high_freq_factor": 0.5,
+ "low_freq_factor": 0.125,
+ "original_max_position_embeddings": 1024,
+ "rope_type": "llama3",
+ }
+ if self.head_dim is None:
+ self.head_dim = self.hidden_size // self.num_attention_heads
+ if self.num_key_value_heads is None:
+ self.num_key_value_heads = self.num_attention_heads
+
+ super().__post_init__(**kwargs)
+
+ def validate_architecture(self):
+ """Part of `@strict`-powered validation. Validates the architecture of the config."""
+ if self.hidden_size % self.num_attention_heads != 0:
+ raise ValueError(
+ f"The hidden size ({self.hidden_size}) is not a multiple of the number of attention "
+ f"heads ({self.num_attention_heads})."
+ )
+
+
+__all__ = ["HiggsAudioV2Config"]
diff --git a/third_party/transformers/src/transformers/models/higgs_audio_v2/convert_higgs_audio_v2_to_hf.py b/third_party/transformers/src/transformers/models/higgs_audio_v2/convert_higgs_audio_v2_to_hf.py
new file mode 100644
index 0000000000000000000000000000000000000000..c041bb54c2b84db5c13de314ad66ed71b06c3951
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/higgs_audio_v2/convert_higgs_audio_v2_to_hf.py
@@ -0,0 +1,200 @@
+# Copyright 2025 BosonAI and The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import glob
+import re
+
+import safetensors.torch
+import torch
+from huggingface_hub import snapshot_download
+
+from transformers import (
+ AutoTokenizer,
+ DacFeatureExtractor,
+ HiggsAudioV2Config,
+ HiggsAudioV2ForConditionalGeneration,
+ HiggsAudioV2Processor,
+ HiggsAudioV2TokenizerModel,
+)
+
+
+CHAT_TEMPLATE = "{{- bos_token }}\n{#- This block extracts the system message, so we can slot it into the right place. #}\n{%- if messages[0]['role'] == 'system' %}\n {%- if messages[0]['content'] is string %}\n {%- set system_message = messages[0]['content']|trim %}\n {%- elif messages[0]['content'] is iterable and messages[0]['content'][0]['type'] == 'text' %}\n {%- set system_message = messages[0]['content'][0]['text']|trim %}\n {%- else %}\n {{- raise_exception(\"System message content must be a string or contain text type!\") }}\n {%- endif %}\n {%- set messages = messages[1:] %}\n{%- else %}\n {{- raise_exception(\"A system message is required but not provided!\") }}\n{%- endif %}\n\n{#- System message #}\n{{- \"<|start_header_id|>system<|end_header_id|>\\n\\n\" }}\n{{- system_message }}\n\n{#- Check for scene message and handle it specially #}\n{%- if messages and messages[0]['role'] == 'scene' %}\n {{- \"\\n\\n<|scene_desc_start|>\\n\" }}\n {%- if messages[0]['content'] is string %}\n {{- messages[0]['content'] | trim }}\n {%- elif messages[0]['content'] is iterable %}\n {%- for content_item in messages[0]['content'] %}\n {%- if content_item['type'] == 'text' %}\n {%- set text_content = content_item['text'] | trim %}\n {{- text_content }}\n {%- if loop.first and not loop.last %}\n {{- \"\\n\\n\" }}\n {%- endif %}\n {%- if not loop.first and not loop.last and messages[0]['content'][loop.index]['type'] != 'audio' %}\n {{- \"\\n\" }}\n {%- endif %}\n {%- elif content_item['type'] == 'audio' %}\n {{- ' <|audio_out_bos|><|AUDIO_OUT|><|audio_eos|>' }}\n {%- if not loop.last %}\n {{- \"\\n\" }}\n {%- endif %}\n {%- endif %}\n {%- endfor %}\n {%- endif %}\n {{- \"\\n<|scene_desc_end|>\" }}\n {%- set messages = messages[1:] %}\n{%- endif %}\n\n{{- \"<|eot_id|>\" }}\n\n{#- Loop through all messages #}\n{%- for message in messages %}\n {{- '<|start_header_id|>' + message['role'] + '<|end_header_id|>\\n\\n' }}\n {%- if message['role'] == 'assistant' %}\n {%- if message['content'] is not iterable or message['content'][0]['type'] != 'audio' %}\n {{- raise_exception(\"Assistant messages must contain audio content only!\") }}\n {%- endif %}\n {{- '<|audio_out_bos|><|AUDIO_OUT|><|audio_eos|>' }}\n {%- else %}\n {%- if message['content'] is string %}\n {{- message['content'] | trim }}\n {%- elif message['content'] is iterable %}\n {%- for content_item in message['content'] %}\n {%- if content_item['type'] == 'text' %}\n {{- content_item['text'] | trim }}\n {%- endif %}\n {%- endfor %}\n {%- endif %}\n {%- endif %}\n {{- '<|eot_id|>' }}\n{%- endfor %}\n\n{%- if add_generation_prompt %}\n {{- '<|start_header_id|>assistant<|end_header_id|>\\n\\n<|audio_out_bos|>' }}\n{%- endif %}"
+
+KEYS_TO_IGNORE = {
+ "audio_codebook_weights",
+ "rotary_emb.inv_freq",
+ "rotary_emb.original_inv_freq",
+}
+
+# fmt: off
+ORIGINAL_TO_CONVERTED_KEY_MAPPING = {
+ r"^audio_codebook_embeddings\.": "model.embed_audio_tokens.embed_audio_tokens.",
+ r"^(embed_tokens|layers|norm)": r"model.\1",
+ r"^audio_decoder_proj\.": "",
+}
+# fmt: on
+
+
+def convert_key(key, mapping):
+ for pattern, replacement in mapping.items():
+ key = re.sub(pattern, replacement, key)
+ return key
+
+
+def convert_model(input_path_or_repo, revision=None):
+ original_directory = snapshot_download(
+ repo_id=input_path_or_repo, revision=revision, allow_patterns=["*.safetensors"]
+ )
+
+ # Load and merge original state dict
+ original_state_dict = {}
+ for path in sorted(glob.glob(f"{original_directory}/*.safetensors")):
+ with safetensors.torch.safe_open(path, framework="pt", device="cpu") as f:
+ for key in f.keys():
+ original_state_dict[key] = f.get_tensor(key)
+
+ # Merge PartiallyFrozenEmbedding weights
+ if "embed_tokens.embedding_frozen.weight" in original_state_dict:
+ original_state_dict["embed_tokens.weight"] = torch.cat(
+ [
+ original_state_dict.pop("embed_tokens.embedding_frozen.weight"),
+ original_state_dict.pop("embed_tokens.embedding_trainable.weight"),
+ ],
+ dim=0,
+ )
+
+ # Merge PartiallyFrozenLinear weights for audio_lm_head
+ if "audio_decoder_proj.audio_lm_head.linear_frozen.weight" in original_state_dict:
+ original_state_dict["audio_decoder_proj.audio_lm_head.weight"] = torch.cat(
+ [
+ original_state_dict.pop("audio_decoder_proj.audio_lm_head.linear_frozen.weight"),
+ original_state_dict.pop("audio_decoder_proj.audio_lm_head.linear_trainable.weight"),
+ ],
+ dim=0,
+ )
+
+ # Merge PartiallyFrozenLinear weights for text_lm_head
+ if "audio_decoder_proj.text_lm_head.linear_frozen.weight" in original_state_dict:
+ original_state_dict["audio_decoder_proj.text_lm_head.weight"] = torch.cat(
+ [
+ original_state_dict.pop("audio_decoder_proj.text_lm_head.linear_frozen.weight"),
+ original_state_dict.pop("audio_decoder_proj.text_lm_head.linear_trainable.weight"),
+ ],
+ dim=0,
+ )
+
+ # Convert keys
+ state_dict = {}
+ for key, tensor in original_state_dict.items():
+ if any(key.endswith(ignored) for ignored in KEYS_TO_IGNORE):
+ continue
+ state_dict[convert_key(key, ORIGINAL_TO_CONVERTED_KEY_MAPPING)] = tensor
+
+ # Keep audio_decoder_proj-prefixed lm_head weights alongside the stripped versions
+ if "audio_lm_head.weight" in state_dict:
+ state_dict["audio_decoder_proj.audio_lm_head.weight"] = state_dict["audio_lm_head.weight"]
+ if "text_lm_head.weight" in state_dict:
+ state_dict["audio_decoder_proj.text_lm_head.weight"] = state_dict["text_lm_head.weight"]
+
+ # Load into model (use_text_head=True to include text_lm_head)
+ config = HiggsAudioV2Config(codebook_size=1026)
+ with torch.device("meta"):
+ model = HiggsAudioV2ForConditionalGeneration(config, use_text_head=True)
+ model._keys_to_ignore_on_load_unexpected = [
+ "audio_decoder_proj.audio_lm_head.weight",
+ "audio_decoder_proj.text_lm_head.weight",
+ ]
+ model.load_state_dict(state_dict, strict=False, assign=True)
+
+ model.generation_config._from_model_config = False
+ model.generation_config.bos_token_id = 1
+ model.generation_config.eos_token_id = 128009
+ model.generation_config.pad_token_id = 128001
+ model.generation_config.ras_win_len = 7
+ model.generation_config.ras_win_max_num_repeat = 2
+ model.generation_config.use_text_head = True
+
+ print("Model converted successfully.")
+
+ return model
+
+
+def create_processor(
+ input_path_or_repo, audio_tokenizer_path_or_repo, input_revision=None, audio_tokenizer_revision=None
+):
+ tokenizer = AutoTokenizer.from_pretrained(input_path_or_repo, revision=input_revision)
+ tokenizer.pad_token = tokenizer.eos_token
+ feature_extractor = DacFeatureExtractor(
+ feature_size=1,
+ hop_length=1,
+ padding_side="right",
+ padding_value=0.0,
+ sampling_rate=24000,
+ return_attention_mask=True,
+ )
+ audio_tokenizer = HiggsAudioV2TokenizerModel.from_pretrained(
+ audio_tokenizer_path_or_repo, revision=audio_tokenizer_revision
+ )
+
+ processor = HiggsAudioV2Processor(
+ feature_extractor=feature_extractor,
+ tokenizer=tokenizer,
+ audio_tokenizer=audio_tokenizer,
+ chat_template=CHAT_TEMPLATE,
+ audio_token="<|AUDIO_OUT|>",
+ audio_bos_token="<|audio_out_bos|>",
+ audio_eos_token="<|audio_eos|>",
+ audio_stream_bos_id=1024,
+ audio_stream_eos_id=1025,
+ )
+ print("Processor created successfully.")
+
+ return processor
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--input_path_or_repo", default="bosonai/higgs-audio-v2-generation-3B-base")
+ parser.add_argument("--input_revision", type=str, default="10840182ca4ad5d9d9113b60b9bb3c1ef1ba3f84")
+ parser.add_argument("--output_dir", type=str, default=None)
+ parser.add_argument("--push_to_hub_path", type=str, default=None)
+ parser.add_argument("--audio_tokenizer_path_or_repo", default="eustlb/higgs-audio-v2-tokenizer")
+ parser.add_argument("--audio_tokenizer_revision", type=str, default="todo")
+ args = parser.parse_args()
+
+ if args.output_dir is None and args.push_to_hub_path is None:
+ raise ValueError("Either --output_dir or --push_to_hub_path must be provided.")
+
+ model = convert_model(args.input_path_or_repo, revision=args.input_revision)
+ processor = create_processor(
+ args.input_path_or_repo,
+ args.audio_tokenizer_path_or_repo,
+ input_revision=args.input_revision,
+ audio_tokenizer_revision=args.audio_tokenizer_revision,
+ )
+
+ if args.output_dir is not None:
+ model.save_pretrained(args.output_dir)
+ processor.save_pretrained(args.output_dir)
+ print(f"Model and processor saved to {args.output_dir}")
+
+ if args.push_to_hub_path is not None:
+ model.push_to_hub(args.push_to_hub_path)
+ processor.push_to_hub(args.push_to_hub_path)
+ print(f"Model and processor pushed to {args.push_to_hub_path}")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/third_party/transformers/src/transformers/models/higgs_audio_v2/generation_higgs_audio_v2.py b/third_party/transformers/src/transformers/models/higgs_audio_v2/generation_higgs_audio_v2.py
new file mode 100644
index 0000000000000000000000000000000000000000..c02aaed939c612625c00c3b699ae1b1b7f6383db
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/higgs_audio_v2/generation_higgs_audio_v2.py
@@ -0,0 +1,445 @@
+# Copyright 2025, The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from dataclasses import dataclass
+from typing import Any, Optional
+
+import torch
+import torch.nn as nn
+
+from ...generation import (
+ GenerateDecoderOnlyOutput,
+ GenerationConfig,
+ GenerationMixin,
+ GenerationMode,
+ LogitsProcessorList,
+ StoppingCriteriaList,
+)
+from ...generation.logits_process import (
+ InfNanRemoveLogitsProcessor,
+ LogitsProcessor,
+ TemperatureLogitsWarper,
+ TopKLogitsWarper,
+ TopPLogitsWarper,
+)
+from ...generation.streamers import BaseStreamer
+from ...generation.utils import GenerateNonBeamOutput
+from ...utils import add_start_docstrings, logging
+
+
+logger = logging.get_logger(__name__)
+
+
+LOGITS_PROCESSOR_INPUTS_DOCSTRING = r"""
+ Args:
+ input_ids (`torch.LongTensor` of shape `(batch_size, sequence_length)`):
+ Indices of input sequence tokens in the vocabulary. [What are input IDs?](../glossary#input-ids)
+ scores (`torch.FloatTensor` of shape `(batch_size, config.vocab_size)`):
+ Prediction scores of a language modeling head. These can be logits for each vocabulary when not using beam
+ search or log softmax for each vocabulary token when using beam search
+
+ Return:
+ `torch.FloatTensor` of shape `(batch_size, config.vocab_size)`: The processed prediction scores.
+
+"""
+
+
+class HiggsAudioV2DelayPatternLogitsProcessor(LogitsProcessor):
+ r"""
+ [`LogitsProcessor`] for Higgs Audio V2 text-to-speech model to handle codebook delay pattern.
+
+
+
+ This logits processor is exclusively compatible with
+ [Higgs Audio V2](https://huggingface.co/docs/transformers/main/en/model_doc/higgs_audio_v2)
+
+
+
+ Args:
+ delay_pattern (list[int]):
+ The delay pattern for the audio bos and eos tokens.
+ audio_bos_token_id (int):
+ The id of the audio bos token.
+ audio_eos_token_id (int):
+ The id of the audio eos token.
+ audio_stream_bos_id (int):
+ The id of the audio stream bos token.
+ audio_stream_eos_id (int):
+ The id of the audio stream eos token.
+ num_codebooks (int):
+ The number of codebooks in the audio stream.
+ codebook_size (int):
+ The size of each codebook in the audio stream.
+ """
+
+ def __init__(
+ self,
+ delay_pattern: list[int],
+ audio_bos_token_id: int,
+ audio_eos_token_id: int,
+ audio_stream_bos_id: int,
+ audio_stream_eos_id: int,
+ num_codebooks: int,
+ codebook_size: int,
+ ):
+ self.delay_pattern = torch.tensor(delay_pattern)
+ self.audio_bos_token_id = audio_bos_token_id
+ self.audio_eos_token_id = audio_eos_token_id
+ self.audio_stream_bos_id = audio_stream_bos_id
+ self.audio_stream_eos_id = audio_stream_eos_id
+ self.num_codebooks = num_codebooks
+ self.codebook_size = codebook_size
+ self.bos_delay_pattern = None
+ self.eos_delay_pattern = None
+ self.vocab_mask_bos = torch.arange(codebook_size) != audio_stream_bos_id
+ self.vocab_mask_eos = torch.arange(codebook_size) != audio_stream_eos_id
+
+ @add_start_docstrings(LOGITS_PROCESSOR_INPUTS_DOCSTRING)
+ def __call__(self, input_ids: torch.LongTensor, scores: torch.FloatTensor) -> torch.FloatTensor:
+ scores = scores.clone().reshape(-1, self.num_codebooks, self.codebook_size)
+ batch_size = scores.shape[0]
+
+ # we only look at the n-th last tokens to initialize the bos and eos delay patterns, where n is the delay pattern size
+ delay_pattern_size = len(self.delay_pattern)
+ input_ids = input_ids[:, -delay_pattern_size:]
+
+ # Initialize bos delay pattern
+ if self.bos_delay_pattern is None:
+ self.bos_delay_pattern = self.delay_pattern.repeat(batch_size, 1)
+ audio_bos_idxs = (input_ids == self.audio_bos_token_id).nonzero()
+
+ if len(audio_bos_idxs) > 0:
+ batch_idxs = audio_bos_idxs[:, 0]
+ is_first = torch.cat([batch_idxs.new_ones(1, dtype=torch.bool), batch_idxs[1:] != batch_idxs[:-1]])
+ min_bos_idxs = audio_bos_idxs[is_first]
+ current_after_bos = (delay_pattern_size - min_bos_idxs[:, 1]).unsqueeze(-1)
+ unique_batch_idxs = batch_idxs.unique().to(self.bos_delay_pattern.device)
+ self.bos_delay_pattern[unique_batch_idxs] = self.bos_delay_pattern[
+ unique_batch_idxs
+ ] - current_after_bos.to(self.bos_delay_pattern.device)
+ else:
+ # there is no audio bos token,
+ self.bos_delay_pattern = torch.zeros_like(self.bos_delay_pattern)
+
+ # Initialize eos delay pattern
+ if self.eos_delay_pattern is None:
+ self.eos_delay_pattern = self.delay_pattern.repeat(batch_size, 1)
+ audio_eos_idxs = (input_ids == self.audio_eos_token_id).nonzero()
+
+ if len(audio_eos_idxs) > 0:
+ batch_idxs = audio_eos_idxs[:, 0]
+ is_first = torch.cat([batch_idxs.new_ones(1, dtype=torch.bool), batch_idxs[1:] != batch_idxs[:-1]])
+ min_eos_idxs = audio_eos_idxs[is_first]
+ current_after_eos = (delay_pattern_size - min_eos_idxs[:, 1]).unsqueeze(-1)
+ unique_batch_idxs = batch_idxs.unique().to(self.eos_delay_pattern.device)
+ self.eos_delay_pattern[unique_batch_idxs] = self.eos_delay_pattern[
+ unique_batch_idxs
+ ] - current_after_eos.to(self.eos_delay_pattern.device)
+
+ # at each generation step, we decrement the bos delay pattern
+ row_mask = self.bos_delay_pattern >= 0
+ scores[(row_mask[..., None] & self.vocab_mask_bos).to(scores.device)] = -float("inf")
+ self.bos_delay_pattern[row_mask] -= 1
+
+ # when the audio eos token is generated, we decrement the eos delay pattern
+ self.eos_delay_pattern[input_ids[:, -1].to(self.eos_delay_pattern.device) == self.audio_eos_token_id] -= 1
+ row_mask = self.eos_delay_pattern <= 0
+ scores[(row_mask[..., None] & self.vocab_mask_eos).to(scores.device)] = -float("inf")
+
+ return scores.reshape(-1, self.codebook_size)
+
+
+@dataclass
+class HiggsAudioV2GenerationOutput(GenerateDecoderOnlyOutput):
+ """
+ Outputs of HiggsAudioV2 generation models, when using non-beam methods.
+
+ Args:
+ sequences (`torch.LongTensor` of shape `(batch_size, audio_sequence_length, num_codebooks)`):
+ The generated text sequences. The second dimension (sequence_length) is either equal to `max_length` or shorter
+ if all batches finished early due to the `eos_token_id`.
+ scores (`tuple(torch.FloatTensor)` *optional*, returned when `output_scores=True`):
+ Processed prediction scores of the language modeling head (scores for each vocabulary token before SoftMax)
+ at each generation step. Tuple of `torch.FloatTensor` with up to `max_new_tokens` elements (one element for
+ each generated token).
+ If the generated token is a text token, the tensor will have shape `(batch_size, config.vocab_size)`.
+ If the generated token is an audio token, the tensor will have shape `(config.num_codebooks, self.model.codebook_size)`
+ logits (`tuple(torch.FloatTensor)` *optional*, returned when `output_logits=True`):
+ Unprocessed prediction scores of the language modeling head or the audio head (scores for each vocabulary token before SoftMax)
+ at each generation step. Tuple of `torch.FloatTensor` with up to `max_new_tokens` elements (one element for
+ each generated token).
+ If the generated token is a text token, the tensor will have shape `(batch_size, config.vocab_size)`.
+ If the generated token is an audio token, the tensor will have shape `(config.num_codebooks, self.model.codebook_size)`
+ attentions (`tuple(tuple(torch.FloatTensor))`, *optional*, returned when `output_attentions=True`):
+ Tuple (one element for each generated token) of tuples (one element for each layer of the decoder) of
+ `torch.FloatTensor` of shape `(batch_size, num_heads, generated_length, sequence_length)`.
+ hidden_states (`tuple(tuple(torch.FloatTensor))`, *optional*):
+ Tuple (one element for each generated token) of tuples (one element for each layer of the decoder) of
+ `torch.FloatTensor` of shape `(batch_size, generated_length, hidden_size)`.
+ past_key_values (`tuple(tuple(torch.FloatTensor)))`, *optional*, returned when `use_cache=True`):
+ Returns the model cache, used to speed up decoding. Different models have a different cache format, check
+ the model's documentation. Usually, a [`~cache_utils.Cache`] instance.
+ audio_sequences (`tuple(torch.LongTensor)` *optional*):
+ The generated discrete audio codes.
+ """
+
+ audio_sequences: list[torch.LongTensor] | None = None
+
+
+class HiggsAudioV2GenerationMixin(GenerationMixin):
+ # Logits processors that only operate on scores and are safe to apply per-codebook.
+ # Other processors (e.g. RepetitionPenaltyLogitsProcessor) use input_ids to index into
+ # scores and are incompatible with audio codebook logits.
+ _supported_logits_processor_types = (
+ TemperatureLogitsWarper,
+ TopKLogitsWarper,
+ TopPLogitsWarper,
+ InfNanRemoveLogitsProcessor,
+ )
+
+ def _get_logits_processor(self, *args, **kwargs):
+ parent_processors = super()._get_logits_processor(*args, **kwargs)
+
+ unsupported = [p for p in parent_processors if not isinstance(p, self._supported_logits_processor_types)]
+ if unsupported:
+ unsupported_names = [type(p).__name__ for p in unsupported]
+ raise ValueError(
+ f"HiggsAudioV2 generates audio codebook logits, not text logits. "
+ f"The following logits processors are not compatible: {unsupported_names}. "
+ f"Only the following processors are supported: "
+ f"{[t.__name__ for t in self._supported_logits_processor_types]}."
+ )
+
+ delay_pattern_processor = HiggsAudioV2DelayPatternLogitsProcessor(
+ delay_pattern=[el + 1 for el in range(self.config.num_codebooks)],
+ audio_bos_token_id=self.config.audio_bos_token_id,
+ audio_eos_token_id=self.config.audio_delay_token_id,
+ audio_stream_bos_id=self.config.audio_stream_bos_id,
+ audio_stream_eos_id=self.config.audio_stream_eos_id,
+ num_codebooks=self.config.num_codebooks,
+ codebook_size=self.config.codebook_size,
+ )
+
+ # The delay pattern processor must run first: it reshapes scores from flat
+ # (batch_size, num_codebooks * codebook_size) to per-codebook (batch_size * num_codebooks, codebook_size).
+ # The sampling warpers (temperature, top_k, top_p) then correctly apply per-codebook.
+ # Without this ordering, top_k/top_p would filter across all codebooks combined,
+ # zeroing out entire codebooks and producing NaN after softmax.
+ logits_processor = LogitsProcessorList()
+ logits_processor.append(delay_pattern_processor)
+ logits_processor.extend(parent_processors)
+ return logits_processor
+
+ def _prepare_generation_config(
+ self, generation_config: GenerationConfig | None, **kwargs: Any
+ ) -> tuple[GenerationConfig, dict]:
+ generation_config, model_kwargs = super()._prepare_generation_config(generation_config, **kwargs)
+ original_get_generation_mode = generation_config.get_generation_mode
+
+ def patched_get_generation_mode(assistant_model=None):
+ generation_mode = original_get_generation_mode(assistant_model)
+ if generation_mode not in [GenerationMode.GREEDY_SEARCH, GenerationMode.SAMPLE]:
+ raise ValueError(
+ f"Generation mode {generation_mode} is not supported for HiggsAudioV2 model. Please set generation parameters to use greedy or sampling generation."
+ )
+
+ return generation_mode
+
+ generation_config.get_generation_mode = patched_get_generation_mode
+
+ return generation_config, model_kwargs
+
+ def _sample(
+ self,
+ input_ids: torch.LongTensor,
+ logits_processor: LogitsProcessorList,
+ stopping_criteria: StoppingCriteriaList,
+ generation_config: GenerationConfig,
+ synced_gpus: bool = False,
+ streamer: Optional["BaseStreamer"] = None,
+ **model_kwargs,
+ ) -> GenerateNonBeamOutput | torch.LongTensor:
+ output_attentions = generation_config.output_attentions
+ output_hidden_states = generation_config.output_hidden_states
+ output_scores = generation_config.output_scores
+ output_logits = generation_config.output_logits
+ return_dict_in_generate = generation_config.return_dict_in_generate
+ has_eos_stopping_criteria = any(hasattr(criteria, "eos_token_id") for criteria in stopping_criteria)
+ do_sample = generation_config.do_sample
+
+ # init attention / hidden states / scores tuples
+ scores = () if (return_dict_in_generate and output_scores) else None
+ raw_logits = () if (return_dict_in_generate and output_logits) else None
+ decoder_attentions = () if (return_dict_in_generate and output_attentions) else None
+ decoder_hidden_states = () if (return_dict_in_generate and output_hidden_states) else None
+
+ # keep track of which sequences are already finished
+ batch_size, cur_len = input_ids.shape[:2]
+ this_peer_finished = False
+ unfinished_sequences = torch.ones(batch_size, dtype=torch.long, device=input_ids.device)
+
+ model_forward = (
+ self.get_compiled_call(generation_config.compile_config)
+ if self._valid_auto_compile_criteria(model_kwargs, generation_config)
+ else self.__call__
+ )
+
+ prefill_consumed = False
+ outputs = self._prefill(
+ input_ids,
+ generation_config,
+ model_kwargs,
+ is_first_iteration=not generation_config.is_assistant,
+ )
+
+ while self._has_unfinished_sequences(this_peer_finished, synced_gpus, device=input_ids.device):
+ if prefill_consumed:
+ next_sequence_length = 1 if model_kwargs["use_cache"] else None
+ model_inputs = self.prepare_inputs_for_generation(
+ input_ids, next_sequence_length=next_sequence_length, **model_kwargs
+ )
+ with self._optimize_model_for_decode():
+ outputs = model_forward(**model_inputs, return_dict=True)
+ prefill_consumed = True
+ model_kwargs = self._update_model_kwargs_for_generation(
+ outputs,
+ model_kwargs,
+ is_encoder_decoder=self.config.is_encoder_decoder,
+ )
+ if synced_gpus and this_peer_finished:
+ continue
+
+ # Copy is needed to avoid keeping a hanging ref to outputs.logits which may be very large for first iteration
+ # (the clone itself is always small)
+ next_token_logits = outputs.logits[:, -1, :].to(copy=True, dtype=torch.float32, device=input_ids.device)
+
+ # pre-process distribution (delay pattern reshapes to per-codebook, then warpers apply per-codebook)
+ next_token_scores = logits_processor(input_ids, next_token_logits)
+
+ # ===========================
+ # BELOW DIFFERENCES WITH GenerationMixin._sample()
+ # Store scores, attentions and hidden_states when required
+ if return_dict_in_generate:
+ if output_scores:
+ scores += (
+ next_token_scores.reshape(batch_size, self.config.num_codebooks, self.config.codebook_size),
+ )
+ if output_logits:
+ raw_logits += (next_token_logits,)
+ if output_attentions:
+ decoder_attentions += (outputs.attentions,)
+ if output_hidden_states:
+ decoder_hidden_states += (outputs.hidden_states,)
+
+ # token selection
+ if do_sample:
+ probs = nn.functional.softmax(next_token_scores, dim=-1)
+ # TODO (joao): this OP throws "skipping cudagraphs due to ['incompatible ops']", find solution
+ next_tokens = torch.multinomial(probs, num_samples=1).squeeze(1)
+ else:
+ next_tokens = torch.argmax(next_token_scores, dim=-1)
+
+ next_token_logits = next_token_logits.reshape(-1, self.config.num_codebooks, self.config.codebook_size)
+ next_tokens = next_tokens.reshape(batch_size, self.config.num_codebooks)
+
+ ras_win_len = generation_config.ras_win_len if hasattr(generation_config, "ras_win_len") else None
+ ras_win_max_num_repeat = (
+ generation_config.ras_win_max_num_repeat
+ if hasattr(generation_config, "ras_win_max_num_repeat")
+ else None
+ )
+ audio_input_ids = model_kwargs.get("audio_input_ids")
+ if ras_win_len is not None and ras_win_max_num_repeat is not None and audio_input_ids is not None:
+ # check if there are repetitions over a window of tokens.
+ audio_inputs_ids_window = audio_input_ids[:, -ras_win_len:, :]
+ repetition_mask = audio_inputs_ids_window == next_tokens.unsqueeze(1)
+
+ # avoid counting the repetition of the audio stream EOS and BOS tokens
+ not_excluded_mask = (audio_inputs_ids_window != self.config.audio_stream_bos_id) & (
+ audio_inputs_ids_window != self.config.audio_stream_eos_id
+ )
+ repetition_mask = repetition_mask & not_excluded_mask
+ rep_num = repetition_mask.sum(dim=1)
+
+ # if we saw repeated tokens in the most recent window of tokens, resample without temperature.
+ replacement_mask = rep_num >= ras_win_max_num_repeat
+ replacement_tokens = (
+ next_token_logits[replacement_mask].softmax(dim=-1).multinomial(1, replacement=True).view(-1)
+ )
+ next_tokens[replacement_mask] = replacement_tokens
+
+ # finished sentences should have their next token be a padding token
+ if has_eos_stopping_criteria:
+ next_tokens = next_tokens * unfinished_sequences[:, None] + self.config.audio_stream_eos_id * (
+ 1 - unfinished_sequences[:, None]
+ )
+
+ has_audio_stream_eos = (next_tokens == self.config.audio_stream_eos_id).any(dim=-1)
+ has_all_audio_stream_eos = (next_tokens == self.config.audio_stream_eos_id).all(dim=-1)
+ next_tokens = next_tokens[:, None, :]
+
+ if audio_input_ids is not None:
+ model_kwargs["audio_input_ids"] = torch.cat([audio_input_ids, next_tokens], dim=1)
+ else:
+ model_kwargs["audio_input_ids"] = next_tokens
+
+ next_audio_input_ids_mask = torch.ones((batch_size, 1), dtype=torch.bool, device=next_tokens.device)
+ next_audio_input_ids_mask[has_all_audio_stream_eos] = 0
+ audio_input_ids_mask = model_kwargs.get("audio_input_ids_mask")
+ if audio_input_ids_mask is not None:
+ model_kwargs["audio_input_ids_mask"] = torch.cat(
+ [audio_input_ids_mask, next_audio_input_ids_mask], dim=1
+ )
+ else:
+ model_kwargs["audio_input_ids_mask"] = next_audio_input_ids_mask
+
+ # generation of a stream eos audio token will start delay pattern masking in the logits processor
+ # for that, we need to set next text token to audio_eos_start_delay_token_id
+ next_tokens_flat = input_ids.new_ones(batch_size) * self.config.audio_token_id
+ next_tokens_flat[has_audio_stream_eos | (input_ids[:, -1] == self.config.audio_delay_token_id)] = (
+ self.config.audio_delay_token_id
+ )
+ if self.config.eos_token_id is not None:
+ next_tokens_flat[has_all_audio_stream_eos] = self.config.eos_token_id
+ next_tokens = next_tokens_flat
+ # ============================
+
+ # update generated ids, model inputs, and length for next step
+ input_ids = torch.cat([input_ids, next_tokens[:, None]], dim=-1)
+ if streamer is not None:
+ streamer.put(next_tokens.cpu())
+
+ unfinished_sequences = unfinished_sequences & ~stopping_criteria(input_ids, scores)
+ this_peer_finished = unfinished_sequences.max() == 0
+ cur_len += 1
+
+ # This is needed to properly delete outputs.logits which may be very large for first iteration
+ # Otherwise a reference to outputs is kept which keeps the logits alive in the next iteration
+ del outputs
+
+ if streamer is not None:
+ streamer.end()
+
+ if return_dict_in_generate:
+ return HiggsAudioV2GenerationOutput(
+ sequences=input_ids,
+ scores=scores,
+ logits=raw_logits,
+ attentions=decoder_attentions,
+ hidden_states=decoder_hidden_states,
+ past_key_values=model_kwargs.get("past_key_values"),
+ audio_sequences=model_kwargs.get("audio_input_ids"),
+ )
+ else:
+ return model_kwargs.get("audio_input_ids")
diff --git a/third_party/transformers/src/transformers/models/higgs_audio_v2/modeling_higgs_audio_v2.py b/third_party/transformers/src/transformers/models/higgs_audio_v2/modeling_higgs_audio_v2.py
new file mode 100644
index 0000000000000000000000000000000000000000..a0f106167721795ad3809eed5d3457b812806084
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/higgs_audio_v2/modeling_higgs_audio_v2.py
@@ -0,0 +1,796 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/higgs_audio_v2/modular_higgs_audio_v2.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_higgs_audio_v2.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2025 the HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+from collections.abc import Callable
+from typing import Optional
+
+import torch
+import torch.nn as nn
+
+from ... import initialization as init
+from ...activations import ACT2FN
+from ...cache_utils import Cache, DynamicCache
+from ...integrations import use_kernel_forward_from_hub, use_kernel_func_from_hub, use_kernelized_func
+from ...masking_utils import create_causal_mask
+from ...modeling_layers import GradientCheckpointingLayer
+from ...modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast
+from ...modeling_rope_utils import ROPE_INIT_FUNCTIONS, dynamic_rope_update
+from ...modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
+from ...processing_utils import Unpack
+from ...utils import TransformersKwargs, auto_docstring, can_return_tuple, logging
+from ...utils.generic import maybe_autocast
+from ...utils.output_capturing import capture_outputs
+from .configuration_higgs_audio_v2 import HiggsAudioV2Config
+from .generation_higgs_audio_v2 import HiggsAudioV2GenerationMixin
+
+
+logger = logging.get_logger(__name__)
+
+
+class HiggsAudioV2MLP(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+ self.config = config
+ self.hidden_size = config.hidden_size
+ self.intermediate_size = config.intermediate_size
+ self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=config.mlp_bias)
+ self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=config.mlp_bias)
+ self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=config.mlp_bias)
+ self.act_fn = ACT2FN[config.hidden_act]
+
+ def forward(self, x):
+ down_proj = self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
+ return down_proj
+
+
+@use_kernel_forward_from_hub("RMSNorm")
+class HiggsAudioV2RMSNorm(nn.Module):
+ def __init__(self, hidden_size, eps: float = 1e-6) -> None:
+ """
+ HiggsAudioV2RMSNorm is equivalent to T5LayerNorm
+ """
+ super().__init__()
+ self.weight = nn.Parameter(torch.ones(hidden_size))
+ self.variance_epsilon = eps
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ input_dtype = hidden_states.dtype
+ hidden_states = hidden_states.to(torch.float32)
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
+ hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
+ return self.weight * hidden_states.to(input_dtype)
+
+ def extra_repr(self):
+ return f"{tuple(self.weight.shape)}, eps={self.variance_epsilon}"
+
+
+def rotate_half(x):
+ """Rotates half the hidden dims of the input."""
+ x1 = x[..., : x.shape[-1] // 2]
+ x2 = x[..., x.shape[-1] // 2 :]
+ return torch.cat((-x2, x1), dim=-1)
+
+
+@use_kernel_func_from_hub("rotary_pos_emb")
+def apply_rotary_pos_emb(q, k, cos, sin, unsqueeze_dim=1):
+ """Applies Rotary Position Embedding to the query and key tensors.
+
+ Args:
+ q (`torch.Tensor`): The query tensor.
+ k (`torch.Tensor`): The key tensor.
+ cos (`torch.Tensor`): The cosine part of the rotary embedding.
+ sin (`torch.Tensor`): The sine part of the rotary embedding.
+ unsqueeze_dim (`int`, *optional*, defaults to 1):
+ The 'unsqueeze_dim' argument specifies the dimension along which to unsqueeze cos[position_ids] and
+ sin[position_ids] so that they can be properly broadcasted to the dimensions of q and k. For example, note
+ that cos[position_ids] and sin[position_ids] have the shape [batch_size, seq_len, head_dim]. Then, if q and
+ k have the shape [batch_size, heads, seq_len, head_dim], then setting unsqueeze_dim=1 makes
+ cos[position_ids] and sin[position_ids] broadcastable to the shapes of q and k. Similarly, if q and k have
+ the shape [batch_size, seq_len, heads, head_dim], then set unsqueeze_dim=2.
+ Returns:
+ `tuple(torch.Tensor)` comprising of the query and key tensors rotated using the Rotary Position Embedding.
+ """
+ cos = cos.unsqueeze(unsqueeze_dim)
+ sin = sin.unsqueeze(unsqueeze_dim)
+ q_embed = (q * cos) + (rotate_half(q) * sin)
+ k_embed = (k * cos) + (rotate_half(k) * sin)
+ return q_embed, k_embed
+
+
+def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
+ """
+ This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
+ num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
+ """
+ batch, num_key_value_heads, slen, head_dim = hidden_states.shape
+ if n_rep == 1:
+ return hidden_states
+ hidden_states = hidden_states[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim)
+ return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
+
+
+def eager_attention_forward(
+ module: nn.Module,
+ query: torch.Tensor,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ attention_mask: torch.Tensor | None,
+ scaling: float,
+ dropout: float = 0.0,
+ **kwargs: Unpack[TransformersKwargs],
+):
+ key_states = repeat_kv(key, module.num_key_value_groups)
+ value_states = repeat_kv(value, module.num_key_value_groups)
+
+ attn_weights = torch.matmul(query, key_states.transpose(2, 3)) * scaling
+ if attention_mask is not None:
+ attn_weights = attn_weights + attention_mask
+
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query.dtype)
+ attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
+ attn_output = torch.matmul(attn_weights, value_states)
+ attn_output = attn_output.transpose(1, 2).contiguous()
+
+ return attn_output, attn_weights
+
+
+@use_kernelized_func(apply_rotary_pos_emb)
+class HiggsAudioV2Attention(nn.Module):
+ """Multi-headed attention from 'Attention Is All You Need' paper"""
+
+ def __init__(self, config: HiggsAudioV2Config, layer_idx: int):
+ super().__init__()
+ self.config = config
+ self.layer_idx = layer_idx
+ self.head_dim = getattr(config, "head_dim", config.hidden_size // config.num_attention_heads)
+ self.num_key_value_groups = config.num_attention_heads // config.num_key_value_heads
+ self.scaling = self.head_dim**-0.5
+ self.attention_dropout = config.attention_dropout
+ self.is_causal = True
+
+ self.q_proj = nn.Linear(
+ config.hidden_size, config.num_attention_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.k_proj = nn.Linear(
+ config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.v_proj = nn.Linear(
+ config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.o_proj = nn.Linear(
+ config.num_attention_heads * self.head_dim, config.hidden_size, bias=config.attention_bias
+ )
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
+ attention_mask: torch.Tensor | None = None,
+ past_key_values: Cache | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple[torch.Tensor, torch.Tensor]:
+ input_shape = hidden_states.shape[:-1]
+ hidden_shape = (*input_shape, -1, self.head_dim)
+
+ query_states = self.q_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+ key_states = self.k_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+ value_states = self.v_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+
+ cos, sin = position_embeddings
+ query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin)
+
+ if past_key_values is not None:
+ key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx)
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ query_states,
+ key_states,
+ value_states,
+ attention_mask,
+ dropout=0.0 if not self.training else self.attention_dropout,
+ scaling=self.scaling,
+ **kwargs,
+ )
+
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
+ attn_output = self.o_proj(attn_output)
+ return attn_output, attn_weights
+
+
+class HiggsAudioV2DecoderLayer(GradientCheckpointingLayer):
+ def __init__(self, config: HiggsAudioV2Config, layer_idx: int):
+ super().__init__()
+ self.hidden_size = config.hidden_size
+
+ self.self_attn = HiggsAudioV2Attention(config=config, layer_idx=layer_idx)
+
+ self.mlp = HiggsAudioV2MLP(config)
+ self.input_layernorm = HiggsAudioV2RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.post_attention_layernorm = HiggsAudioV2RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+
+ self.audio_mlp = HiggsAudioV2MLP(config)
+ self.audio_input_layernorm = HiggsAudioV2RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.audio_post_attention_layernorm = HiggsAudioV2RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None,
+ attention_mask: torch.Tensor | None = None,
+ audio_token_mask: torch.BoolTensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ use_cache: bool | None = False,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> torch.Tensor:
+ residual = hidden_states
+
+ if audio_token_mask is None:
+ hidden_states = self.audio_input_layernorm(hidden_states)
+ else:
+ audio_token_mask = audio_token_mask.to(hidden_states.device)
+ hidden_states = hidden_states.masked_scatter(
+ audio_token_mask.unsqueeze(-1),
+ self.audio_input_layernorm(hidden_states[audio_token_mask]).to(hidden_states.device),
+ )
+ hidden_states = hidden_states.masked_scatter(
+ ~audio_token_mask.unsqueeze(-1),
+ self.input_layernorm(hidden_states[~audio_token_mask]).to(hidden_states.device),
+ )
+
+ # Self Attention
+ hidden_states, _ = self.self_attn(
+ hidden_states=hidden_states,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ position_embeddings=position_embeddings,
+ **kwargs,
+ )
+ hidden_states = residual + hidden_states
+
+ if audio_token_mask is None:
+ audio_hidden_states = self.audio_post_attention_layernorm(hidden_states)
+ audio_hidden_states = self.audio_mlp(audio_hidden_states)
+ hidden_states = hidden_states + audio_hidden_states.to(hidden_states.device)
+ else:
+ text_hidden_states = self.post_attention_layernorm(hidden_states[~audio_token_mask])
+ audio_hidden_states = self.audio_post_attention_layernorm(hidden_states[audio_token_mask])
+
+ text_hidden_states = self.mlp(text_hidden_states)
+ hidden_states[~audio_token_mask] += text_hidden_states.to(hidden_states.device)
+
+ audio_hidden_states = self.audio_mlp(audio_hidden_states)
+ hidden_states[audio_token_mask] += audio_hidden_states.to(hidden_states.device)
+
+ return hidden_states
+
+
+class HiggsAudioV2Embeddings(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+ self.embed_audio_tokens = nn.Embedding((config.num_codebooks * config.codebook_size), config.hidden_size)
+ self.register_buffer(
+ "audio_tokens_offsets", torch.arange(config.num_codebooks) * config.codebook_size, persistent=False
+ )
+
+ def forward(self, input_ids):
+ inputs_embeds = self.embed_audio_tokens(input_ids + self.audio_tokens_offsets)
+ inputs_embeds = inputs_embeds.sum(dim=-2)
+ return inputs_embeds
+
+
+@auto_docstring
+class HiggsAudioV2PreTrainedModel(PreTrainedModel):
+ config: HiggsAudioV2Config
+ base_model_prefix = "model"
+ supports_gradient_checkpointing = True
+ _no_split_modules = ["HiggsAudioV2DecoderLayer"]
+ _skip_keys_device_placement = ["past_key_values"]
+ _supports_flash_attn = True
+ _supports_sdpa = True
+ _supports_flex_attn = True
+
+ _can_compile_fullgraph = True
+ _supports_attention_backend = True
+ _can_record_outputs = {
+ "hidden_states": HiggsAudioV2DecoderLayer,
+ "attentions": HiggsAudioV2Attention,
+ }
+
+ @torch.no_grad()
+ def _init_weights(self, module):
+ super()._init_weights(module)
+
+ if isinstance(module, HiggsAudioV2Embeddings):
+ init.copy_(
+ module.audio_tokens_offsets, torch.arange(self.config.num_codebooks) * self.config.codebook_size
+ )
+
+
+class HiggsAudioV2RotaryEmbedding(nn.Module):
+ inv_freq: torch.Tensor # fix linting for `register_buffer`
+
+ def __init__(self, config: HiggsAudioV2Config, device=None):
+ super().__init__()
+ self.max_seq_len_cached = config.max_position_embeddings
+ self.original_max_seq_len = config.max_position_embeddings
+
+ self.config = config
+
+ self.rope_type = self.config.rope_parameters["rope_type"]
+ rope_init_fn: Callable = self.compute_default_rope_parameters
+ if self.rope_type != "default":
+ rope_init_fn = ROPE_INIT_FUNCTIONS[self.rope_type]
+ inv_freq, self.attention_scaling = rope_init_fn(self.config, device)
+
+ self.register_buffer("inv_freq", inv_freq, persistent=False)
+ self.register_buffer("original_inv_freq", inv_freq.clone(), persistent=False)
+
+ @staticmethod
+ def compute_default_rope_parameters(
+ config: HiggsAudioV2Config | None = None,
+ device: Optional["torch.device"] = None,
+ seq_len: int | None = None,
+ ) -> tuple["torch.Tensor", float]:
+ """
+ Computes the inverse frequencies according to the original RoPE implementation
+ Args:
+ config ([`~transformers.PreTrainedConfig`]):
+ The model configuration.
+ device (`torch.device`):
+ The device to use for initialization of the inverse frequencies.
+ seq_len (`int`, *optional*):
+ The current sequence length. Unused for this type of RoPE.
+ Returns:
+ Tuple of (`torch.Tensor`, `float`), containing the inverse frequencies for the RoPE embeddings and the
+ post-processing scaling factor applied to the computed cos/sin (unused in this type of RoPE).
+ """
+ base = config.rope_parameters["rope_theta"]
+ dim = getattr(config, "head_dim", None) or config.hidden_size // config.num_attention_heads
+
+ attention_factor = 1.0 # Unused in this type of RoPE
+
+ # Compute the inverse frequencies
+ inv_freq = 1.0 / (
+ base ** (torch.arange(0, dim, 2, dtype=torch.int64).to(device=device, dtype=torch.float) / dim)
+ )
+ return inv_freq, attention_factor
+
+ @torch.no_grad()
+ @dynamic_rope_update # power user: used with advanced RoPE types (e.g. dynamic rope)
+ def forward(self, x, position_ids):
+ inv_freq_expanded = self.inv_freq[None, :, None].float().expand(position_ids.shape[0], -1, 1).to(x.device)
+ position_ids_expanded = position_ids[:, None, :].float()
+
+ device_type = x.device.type if isinstance(x.device.type, str) and x.device.type != "mps" else "cpu"
+ with maybe_autocast(device_type=device_type, enabled=False): # Force float32
+ freqs = (inv_freq_expanded.float() @ position_ids_expanded.float()).transpose(1, 2)
+ emb = torch.cat((freqs, freqs), dim=-1)
+ cos = emb.cos() * self.attention_scaling
+ sin = emb.sin() * self.attention_scaling
+
+ return cos.to(dtype=x.dtype), sin.to(dtype=x.dtype)
+
+
+@auto_docstring
+class HiggsAudioV2Model(HiggsAudioV2PreTrainedModel):
+ def __init__(self, config: HiggsAudioV2Config):
+ super().__init__(config)
+ self.padding_idx = config.pad_token_id
+ self.vocab_size = config.vocab_size
+
+ self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)
+ self.layers = nn.ModuleList(
+ [HiggsAudioV2DecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
+ )
+ self.norm = HiggsAudioV2RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.rotary_emb = HiggsAudioV2RotaryEmbedding(config=config)
+ self.gradient_checkpointing = False
+ self.embed_audio_tokens = HiggsAudioV2Embeddings(config)
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @capture_outputs
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ audio_input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.LongTensor | None = None,
+ audio_input_ids_mask: torch.BoolTensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ use_cache: bool | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> BaseModelOutputWithPast:
+ r"""
+ audio_input_ids (`torch.LongTensor` of shape `(batch_size, num_audio_frames, num_codebooks)`, *optional*):
+ Indices of audio codebook tokens.
+
+ Indices can be obtained using [`HiggsAudioV2TokenizerModel.encode`].
+ audio_input_ids_mask (`torch.BoolTensor` of shape `(batch_size, num_audio_frames)`, *optional*):
+ Indicates which audio frames in `audio_input_ids` are valid.
+
+ Returns:
+ [`~models.modeling_outputs.BaseModelOutputWithPast`]:
+ Usual decoder outputs with the placeholder positions already substituted by their corresponding
+ audio embeddings.
+
+ Example:
+
+ ```python
+ >>> from transformers import AutoProcessor, HiggsAudioV2Model
+ >>> import torch
+ >>> device = "cuda" if torch.cuda.is_available() else "cpu"
+ >>> processor = AutoProcessor.from_pretrained("eustlb/higgs-audio-v2-generation-3B-base", device_map=device)
+ >>> model = HiggsAudioV2Model.from_pretrained("eustlb/higgs-audio-v2-generation-3B-base", device_map=device)
+ >>> conversation = [
+ ... {
+ ... "role": "system",
+ ... "content": [
+ ... {
+ ... "type": "text",
+ ... "text": "Generate audio following instruction."
+ ... }
+ ... ]
+ ... },
+ ... {
+ ... "role": "scene",
+ ... "content": [
+ ... {
+ ... "type": "text",
+ ... "text": "Audio is recorded from a quiet room."
+ ... }
+ ... ]
+ ... },
+ ... {
+ ... "role": "user",
+ ... "content": [
+ ... {
+ ... "type": "text",
+ ... "text": "It was the night before my birthday. Hooray! It's almost here! It may not be a holiday, but it's the best day of the year."
+ ... }
+ ... ]
+ ... },
+ ... {
+ ... "role": "assistant",
+ ... "content": [
+ ... {
+ ... "type": "audio",
+ ... "url": "https://huggingface.co/datasets/eustlb/dummy-audio-samples-higgs/resolve/main/belinda.wav"
+ ... }
+ ... ]
+ ... },
+ ... {
+ ... "role": "user",
+ ... "content": [
+ ... {
+ ... "type": "text",
+ ... "text": "The sun rises in the east and sets in the west. This simple fact has been observed by humans for thousands of years."
+ ... }
+ ... ]
+ ... }
+ ... ]
+ >>> inputs = processor.apply_chat_template(conversation, return_dict=True, tokenize=True, sampling_rate=24000, return_tensors="pt")
+ >>> inputs = inputs.to(model.device)
+ >>> outputs = model(**inputs)
+ ```
+ """
+ if (input_ids is None) and (inputs_embeds is None) and (audio_input_ids is None):
+ raise ValueError("You must specify at least one of input_ids, inputs_embeds, or audio_input_ids")
+
+ if (input_ids is not None) and (inputs_embeds is not None):
+ raise ValueError("Only one of input_ids or inputs_embeds can be provided")
+
+ audio_token_mask = self.get_placeholder_mask(input_ids, inputs_embeds, audio_input_ids_mask)
+
+ if input_ids is not None:
+ inputs_embeds = self.embed_tokens(input_ids)
+
+ if audio_input_ids is not None:
+ audio_embeds = self.embed_audio_tokens(audio_input_ids)
+
+ if inputs_embeds is not None and audio_input_ids is not None:
+ audio_embeds = (
+ audio_embeds[audio_input_ids_mask.to(audio_embeds.device)]
+ if audio_input_ids_mask is not None
+ else audio_embeds
+ )
+ inputs_embeds = inputs_embeds.masked_scatter(
+ audio_token_mask[..., None].expand_as(inputs_embeds), audio_embeds.to(inputs_embeds.device)
+ )
+ elif audio_input_ids is not None:
+ inputs_embeds = audio_embeds
+
+ if use_cache and past_key_values is None:
+ past_key_values = DynamicCache(config=self.config)
+
+ if position_ids is None:
+ past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
+ position_ids = torch.arange(inputs_embeds.shape[1], device=inputs_embeds.device) + past_seen_tokens
+ position_ids = position_ids.unsqueeze(0)
+
+ causal_mask = create_causal_mask(
+ config=self.config,
+ inputs_embeds=inputs_embeds,
+ attention_mask=attention_mask,
+ past_key_values=past_key_values,
+ position_ids=position_ids,
+ )
+
+ hidden_states = inputs_embeds
+ position_embeddings = self.rotary_emb(hidden_states, position_ids)
+
+ for decoder_layer in self.layers[: self.config.num_hidden_layers]:
+ hidden_states = decoder_layer(
+ hidden_states,
+ attention_mask=causal_mask,
+ audio_token_mask=audio_token_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ position_embeddings=position_embeddings,
+ **kwargs,
+ )
+
+ hidden_states = self.norm(hidden_states)
+ return BaseModelOutputWithPast(
+ last_hidden_state=hidden_states,
+ past_key_values=past_key_values,
+ )
+
+ def get_placeholder_mask(
+ self, input_ids: torch.LongTensor, inputs_embeds: torch.FloatTensor, audio_input_ids_mask: torch.LongTensor
+ ):
+ """
+ Obtains multimodal placeholder mask from `input_ids` or `inputs_embeds`, and checks that the placeholder token count is
+ equal to the length of audio_input_ids. If the lengths are different, an error is raised.
+
+ If input_ids and inputs_embeds are None, we return None.
+ Indeed this means we cannot determine the placeholder mask, the model is to be used in a audio-only mode, hence we return None.
+ """
+ if input_ids is None and inputs_embeds is None:
+ return None
+
+ elif input_ids is None:
+ special_audio_mask = inputs_embeds == self.embed_tokens(
+ torch.tensor(self.config.audio_token_id, dtype=torch.long, device=inputs_embeds.device)
+ )
+ special_audio_mask = special_audio_mask.all(-1)
+
+ else:
+ special_audio_mask = (input_ids == self.config.audio_token_id) | (
+ input_ids == self.config.audio_delay_token_id
+ )
+
+ return special_audio_mask
+
+
+@auto_docstring(
+ custom_intro="""
+ The Higgs Audio model, a llama-like auto-regressive transformer model with dual-FFN.
+ """
+)
+class HiggsAudioV2ForConditionalGeneration(HiggsAudioV2PreTrainedModel, HiggsAudioV2GenerationMixin):
+ base_model_prefix = "model"
+ _keys_to_ignore_on_load_unexpected = ["text_lm_head.weight"]
+
+ def __init__(self, config: HiggsAudioV2Config, use_text_head: bool = False):
+ r"""
+ use_text_head (`bool`, *optional*, defaults to False):
+ Whether to use a text language model head. Such head is not required for generation,
+ but can be used to compute the text loss when training.
+ """
+ super().__init__(config)
+ self.model = HiggsAudioV2Model(config)
+ self.audio_lm_head = nn.Linear(config.hidden_size, config.num_codebooks * config.codebook_size, bias=False)
+ self.text_lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False) if use_text_head else None
+
+ self.post_init()
+
+ def prepare_inputs_for_generation(
+ self,
+ input_ids: torch.LongTensor,
+ audio_input_ids: torch.LongTensor | None = None,
+ audio_input_ids_mask: torch.LongTensor | None = None,
+ **kwargs,
+ ):
+ model_inputs = super().prepare_inputs_for_generation(input_ids, **kwargs)
+
+ if audio_input_ids is not None and model_inputs.get("past_key_values") is not None:
+ current_cache_length = model_inputs.get("past_key_values").get_seq_length()
+ audio_token_mask = (input_ids == self.config.audio_token_id) | (
+ input_ids == self.config.audio_delay_token_id
+ )
+ in_cache_num_audio_input_ids = audio_token_mask[:, :current_cache_length].sum(dim=-1)
+
+ # already cached audio_input_ids should be masked
+ # this surmise that audio_input_ids are right padded!
+ valid_audio_input_ids = audio_input_ids_mask.cumsum(dim=-1) > in_cache_num_audio_input_ids[:, None]
+ audio_input_ids_mask = audio_input_ids_mask & valid_audio_input_ids
+
+ if audio_input_ids_mask is not None and (~audio_input_ids_mask[:, :-1]).all():
+ # in decoding mode, we only pass audio_input_ids
+ audio_input_ids = audio_input_ids[:, -1:, :].clone(memory_format=torch.contiguous_format)
+ model_inputs.pop("input_ids", None)
+ audio_input_ids_mask = None
+
+ model_inputs["audio_input_ids"] = audio_input_ids
+ model_inputs["audio_input_ids_mask"] = audio_input_ids_mask
+
+ return model_inputs
+
+ @auto_docstring
+ @can_return_tuple
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.BoolTensor | None = None,
+ audio_input_ids: torch.LongTensor | None = None,
+ audio_input_ids_mask: torch.LongTensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ audio_labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ **kwargs: Unpack[TransformersKwargs],
+ ):
+ r"""
+ audio_input_ids (`torch.LongTensor` of shape `(batch_size, num_audio_frames, num_codebooks)`, *optional*):
+ Indices of audio codebook tokens.
+
+ Indices can be obtained using [`HiggsAudioV2TokenizerModel.encode`].
+ audio_input_ids_mask (`torch.BoolTensor` of shape `(batch_size, num_audio_frames)`, *optional*):
+ Indicates which audio frames in `audio_input_ids` are valid.
+ audio_labels (`torch.LongTensor` of shape `(batch_size, num_audio_frames, num_codebooks)`, *optional*):
+ Labels for the audio codebook tokens for computing the masked language modeling loss. Indices should either be in `[0, ...,
+ config.codebook_size]. Token with indices set to `-100` are ignored (masked), the loss is only computed for the tokens with labels in `[0, ..., config.codebook_size]`.
+ Can be obtained using `output_labels=True` when calling [`HiggsAudioV2Processor`].
+
+ Returns:
+ [`~models.modeling_outputs.CausalLMOutputWithPast`]:
+ A [`~models.modeling_outputs.CausalLMOutputWithPast`] containing the logits, loss (if labels are provided),
+ and other outputs from the model.
+
+ Example:
+
+ ```python
+ >>> from transformers import AutoProcessor, HiggsAudioV2ForConditionalGeneration
+ >>> model_id = "eustlb/higgs-audio-v2-generation-3B-base"
+ >>> processor = AutoProcessor.from_pretrained(model_id, device_map="auto")
+ >>> model = HiggsAudioV2ForConditionalGeneration.from_pretrained(model_id, device_map="auto")
+ >>> conversation = [
+ ... {
+ ... "role": "system",
+ ... "content": [
+ ... {
+ ... "type": "text",
+ ... "text": "Generate audio following instruction."
+ ... }
+ ... ]
+ ... },
+ ... {
+ ... "role": "scene",
+ ... "content": [
+ ... {
+ ... "type": "text",
+ ... "text": "Audio is recorded from a quiet room."
+ ... }
+ ... ]
+ ... },
+ ... {
+ ... "role": "user",
+ ... "content": [
+ ... {
+ ... "type": "text",
+ ... "text": "It was the night before my birthday. Hooray! It's almost here! It may not be a holiday, but it's the best day of the year."
+ ... }
+ ... ]
+ ... },
+ ... {
+ ... "role": "assistant",
+ ... "content": [
+ ... {
+ ... "type": "audio",
+ ... "url": "https://huggingface.co/datasets/eustlb/dummy-audio-samples-higgs/resolve/main/belinda.wav"
+ ... }
+ ... ]
+ ... },
+ ... {
+ ... "role": "user",
+ ... "content": [
+ ... {
+ ... "type": "text",
+ ... "text": "The sun rises in the east and sets in the west. This simple fact has been observed by humans for thousands of years."
+ ... }
+ ... ]
+ ... }
+ ... ]
+ >>> inputs = processor.apply_chat_template(conversation, return_dict=True, tokenize=True, sampling_rate=24000, return_tensors="pt")
+ >>> inputs = inputs.to(model.device)
+ >>> outputs = model(**inputs)
+ ```
+ """
+ outputs = self.model(
+ input_ids=input_ids,
+ attention_mask=attention_mask,
+ audio_input_ids=audio_input_ids,
+ audio_input_ids_mask=audio_input_ids_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ hidden_states = outputs.last_hidden_state
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
+ logits = self.audio_lm_head(hidden_states[:, slice_indices, :])
+
+ loss = None
+ if audio_labels is not None:
+ audio_logits = logits.reshape(*logits.shape[:2], self.config.num_codebooks, self.config.codebook_size)
+ audio_labels_expanded = input_ids.new_ones((*input_ids.shape[:2], 8)) * -100
+ audio_token_mask = self.model.get_placeholder_mask(input_ids, inputs_embeds, audio_input_ids_mask)
+ audio_labels_expanded[audio_token_mask] = audio_labels[audio_input_ids_mask]
+
+ codebook_losses = []
+ for codebook_idx in range(self.config.num_codebooks):
+ codebook_logits = audio_logits[:, :, codebook_idx, :]
+ codebook_labels = audio_labels_expanded[:, :, codebook_idx]
+ codebook_losses.append(
+ self.loss_function(codebook_logits, codebook_labels, self.config.codebook_size, **kwargs)
+ )
+
+ loss = sum(codebook_losses)
+
+ if labels is not None:
+ if self.text_lm_head is not None:
+ text_logits = self.text_lm_head(hidden_states[:, slice_indices, :])
+ text_loss = self.loss_function(text_logits, labels, self.config.vocab_size, **kwargs)
+ loss = text_loss if loss is None else loss + text_loss
+ else:
+ logger.warning_once(
+ f"`labels` provided to {self.__class__.__name__} but `text_lm_head` is disabled. "
+ f"Text labels ignored. Set `use_text_head=True` in model init to enable text loss."
+ )
+
+ return CausalLMOutputWithPast(
+ loss=loss,
+ logits=logits,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ )
+
+
+__all__ = ["HiggsAudioV2ForConditionalGeneration", "HiggsAudioV2PreTrainedModel", "HiggsAudioV2Model"]
diff --git a/third_party/transformers/src/transformers/models/higgs_audio_v2/modular_higgs_audio_v2.py b/third_party/transformers/src/transformers/models/higgs_audio_v2/modular_higgs_audio_v2.py
new file mode 100644
index 0000000000000000000000000000000000000000..03d34b0e344445153e20513be6cb3cb9f43b9580
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/higgs_audio_v2/modular_higgs_audio_v2.py
@@ -0,0 +1,577 @@
+# Copyright 2025 the HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import torch
+import torch.nn as nn
+from huggingface_hub.dataclasses import strict
+
+from ... import initialization as init
+from ...cache_utils import Cache, DynamicCache
+from ...masking_utils import create_causal_mask
+from ...modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast
+from ...modeling_utils import PreTrainedModel
+from ...processing_utils import Unpack
+from ...utils import (
+ TransformersKwargs,
+ auto_docstring,
+ can_return_tuple,
+ logging,
+)
+from ...utils.output_capturing import capture_outputs
+from ..csm.modeling_csm import CsmBackboneModelEmbeddings
+from ..llama.configuration_llama import LlamaConfig
+from ..llama.modeling_llama import LlamaDecoderLayer, LlamaMLP, LlamaModel, LlamaPreTrainedModel, LlamaRMSNorm
+from .generation_higgs_audio_v2 import HiggsAudioV2GenerationMixin
+
+
+logger = logging.get_logger(__name__)
+
+
+@auto_docstring(checkpoint="bosonai/higgs-audio-v2-generation-3B-base")
+@strict
+class HiggsAudioV2Config(LlamaConfig):
+ r"""
+ audio_bos_token_id (`int`, *optional*, defaults to 128013):
+ The token ID for the beginning-of-sequence token for audio output.
+ audio_delay_token_id (`int`, *optional*, defaults to 128014):
+ The token ID used for audio delay pattern in multi-codebook generation.
+ audio_stream_bos_id (`int`, *optional*, defaults to 1024):
+ The ID for the beginning-of-stream token in audio sequences.
+ audio_stream_eos_id (`int`, *optional*, defaults to 1025):
+ The ID for the end-of-stream token in audio sequences.
+
+ Example:
+
+ ```python
+ >>> from transformers import HiggsAudioV2Model, HiggsAudioV2Config
+
+ >>> # Initializing a HiggsAudioV2 style configuration
+ >>> configuration = HiggsAudioV2Config()
+
+ >>> # Initializing a model from the configuration
+ >>> model = HiggsAudioV2Model(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ vocab_size: int = 128256
+ rms_norm_eps: float = 1e-5
+ hidden_size: int = 3072
+ intermediate_size: int = 8192
+ num_hidden_layers: int = 28
+ num_attention_heads: int = 24
+ num_key_value_heads: int = 8
+ pad_token_id: int | None = 128001
+ eos_token_id: int | list[int] | None = 128009
+ head_dim: int | None = 128
+ num_codebooks: int = 8
+ codebook_size: int = 1024
+ audio_token_id: int = 128016
+ audio_bos_token_id: int = 128013
+ audio_delay_token_id: int = 128014
+ audio_stream_bos_id: int = 1024
+ audio_stream_eos_id: int = 1025
+
+ def __post_init__(self, **kwargs):
+ if self.rope_parameters is None:
+ self.rope_parameters = {
+ "factor": 32.0,
+ "rope_theta": 500000.0,
+ "high_freq_factor": 0.5,
+ "low_freq_factor": 0.125,
+ "original_max_position_embeddings": 1024,
+ "rope_type": "llama3",
+ }
+ super().__post_init__(**kwargs)
+
+
+class HiggsAudioV2MLP(LlamaMLP):
+ pass
+
+
+class HiggsAudioV2RMSNorm(LlamaRMSNorm):
+ pass
+
+
+class HiggsAudioV2DecoderLayer(LlamaDecoderLayer):
+ def __init__(self, config: HiggsAudioV2Config, layer_idx: int):
+ super().__init__(config, layer_idx)
+
+ self.audio_mlp = HiggsAudioV2MLP(config)
+ self.audio_input_layernorm = HiggsAudioV2RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.audio_post_attention_layernorm = HiggsAudioV2RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None,
+ attention_mask: torch.Tensor | None = None,
+ audio_token_mask: torch.BoolTensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ use_cache: bool | None = False,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> torch.Tensor:
+ residual = hidden_states
+
+ if audio_token_mask is None:
+ hidden_states = self.audio_input_layernorm(hidden_states)
+ else:
+ audio_token_mask = audio_token_mask.to(hidden_states.device)
+ hidden_states = hidden_states.masked_scatter(
+ audio_token_mask.unsqueeze(-1),
+ self.audio_input_layernorm(hidden_states[audio_token_mask]).to(hidden_states.device),
+ )
+ hidden_states = hidden_states.masked_scatter(
+ ~audio_token_mask.unsqueeze(-1),
+ self.input_layernorm(hidden_states[~audio_token_mask]).to(hidden_states.device),
+ )
+
+ # Self Attention
+ hidden_states, _ = self.self_attn(
+ hidden_states=hidden_states,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ position_embeddings=position_embeddings,
+ **kwargs,
+ )
+ hidden_states = residual + hidden_states
+
+ if audio_token_mask is None:
+ audio_hidden_states = self.audio_post_attention_layernorm(hidden_states)
+ audio_hidden_states = self.audio_mlp(audio_hidden_states)
+ hidden_states = hidden_states + audio_hidden_states.to(hidden_states.device)
+ else:
+ text_hidden_states = self.post_attention_layernorm(hidden_states[~audio_token_mask])
+ audio_hidden_states = self.audio_post_attention_layernorm(hidden_states[audio_token_mask])
+
+ text_hidden_states = self.mlp(text_hidden_states)
+ hidden_states[~audio_token_mask] += text_hidden_states.to(hidden_states.device)
+
+ audio_hidden_states = self.audio_mlp(audio_hidden_states)
+ hidden_states[audio_token_mask] += audio_hidden_states.to(hidden_states.device)
+
+ return hidden_states
+
+
+class HiggsAudioV2Embeddings(CsmBackboneModelEmbeddings):
+ def forward(self, input_ids):
+ inputs_embeds = self.embed_audio_tokens(input_ids + self.audio_tokens_offsets)
+ inputs_embeds = inputs_embeds.sum(dim=-2)
+ return inputs_embeds
+
+
+class HiggsAudioV2PreTrainedModel(LlamaPreTrainedModel, PreTrainedModel):
+ @torch.no_grad()
+ def _init_weights(self, module):
+ PreTrainedModel._init_weights(module)
+
+ if isinstance(module, HiggsAudioV2Embeddings):
+ init.copy_(
+ module.audio_tokens_offsets, torch.arange(self.config.num_codebooks) * self.config.codebook_size
+ )
+
+
+class HiggsAudioV2Model(LlamaModel):
+ def __init__(self, config: HiggsAudioV2Config):
+ super().__init__(config)
+ self.embed_audio_tokens = HiggsAudioV2Embeddings(config)
+
+ def get_placeholder_mask(
+ self, input_ids: torch.LongTensor, inputs_embeds: torch.FloatTensor, audio_input_ids_mask: torch.LongTensor
+ ):
+ """
+ Obtains multimodal placeholder mask from `input_ids` or `inputs_embeds`, and checks that the placeholder token count is
+ equal to the length of audio_input_ids. If the lengths are different, an error is raised.
+
+ If input_ids and inputs_embeds are None, we return None.
+ Indeed this means we cannot determine the placeholder mask, the model is to be used in a audio-only mode, hence we return None.
+ """
+ if input_ids is None and inputs_embeds is None:
+ return None
+
+ elif input_ids is None:
+ special_audio_mask = inputs_embeds == self.embed_tokens(
+ torch.tensor(self.config.audio_token_id, dtype=torch.long, device=inputs_embeds.device)
+ )
+ special_audio_mask = special_audio_mask.all(-1)
+
+ else:
+ special_audio_mask = (input_ids == self.config.audio_token_id) | (
+ input_ids == self.config.audio_delay_token_id
+ )
+
+ return special_audio_mask
+
+ @capture_outputs
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ audio_input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.LongTensor | None = None,
+ audio_input_ids_mask: torch.BoolTensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ use_cache: bool | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> BaseModelOutputWithPast:
+ r"""
+ audio_input_ids (`torch.LongTensor` of shape `(batch_size, num_audio_frames, num_codebooks)`, *optional*):
+ Indices of audio codebook tokens.
+
+ Indices can be obtained using [`HiggsAudioV2TokenizerModel.encode`].
+ audio_input_ids_mask (`torch.BoolTensor` of shape `(batch_size, num_audio_frames)`, *optional*):
+ Indicates which audio frames in `audio_input_ids` are valid.
+
+ Returns:
+ [`~models.modeling_outputs.BaseModelOutputWithPast`]:
+ Usual decoder outputs with the placeholder positions already substituted by their corresponding
+ audio embeddings.
+
+ Example:
+
+ ```python
+ >>> from transformers import AutoProcessor, HiggsAudioV2Model
+ >>> import torch
+ >>> device = "cuda" if torch.cuda.is_available() else "cpu"
+ >>> processor = AutoProcessor.from_pretrained("eustlb/higgs-audio-v2-generation-3B-base", device_map=device)
+ >>> model = HiggsAudioV2Model.from_pretrained("eustlb/higgs-audio-v2-generation-3B-base", device_map=device)
+ >>> conversation = [
+ ... {
+ ... "role": "system",
+ ... "content": [
+ ... {
+ ... "type": "text",
+ ... "text": "Generate audio following instruction."
+ ... }
+ ... ]
+ ... },
+ ... {
+ ... "role": "scene",
+ ... "content": [
+ ... {
+ ... "type": "text",
+ ... "text": "Audio is recorded from a quiet room."
+ ... }
+ ... ]
+ ... },
+ ... {
+ ... "role": "user",
+ ... "content": [
+ ... {
+ ... "type": "text",
+ ... "text": "It was the night before my birthday. Hooray! It's almost here! It may not be a holiday, but it's the best day of the year."
+ ... }
+ ... ]
+ ... },
+ ... {
+ ... "role": "assistant",
+ ... "content": [
+ ... {
+ ... "type": "audio",
+ ... "url": "https://huggingface.co/datasets/eustlb/dummy-audio-samples-higgs/resolve/main/belinda.wav"
+ ... }
+ ... ]
+ ... },
+ ... {
+ ... "role": "user",
+ ... "content": [
+ ... {
+ ... "type": "text",
+ ... "text": "The sun rises in the east and sets in the west. This simple fact has been observed by humans for thousands of years."
+ ... }
+ ... ]
+ ... }
+ ... ]
+ >>> inputs = processor.apply_chat_template(conversation, return_dict=True, tokenize=True, sampling_rate=24000, return_tensors="pt")
+ >>> inputs = inputs.to(model.device)
+ >>> outputs = model(**inputs)
+ ```
+ """
+ if (input_ids is None) and (inputs_embeds is None) and (audio_input_ids is None):
+ raise ValueError("You must specify at least one of input_ids, inputs_embeds, or audio_input_ids")
+
+ if (input_ids is not None) and (inputs_embeds is not None):
+ raise ValueError("Only one of input_ids or inputs_embeds can be provided")
+
+ audio_token_mask = self.get_placeholder_mask(input_ids, inputs_embeds, audio_input_ids_mask)
+
+ if input_ids is not None:
+ inputs_embeds = self.embed_tokens(input_ids)
+
+ if audio_input_ids is not None:
+ audio_embeds = self.embed_audio_tokens(audio_input_ids)
+
+ if inputs_embeds is not None and audio_input_ids is not None:
+ audio_embeds = (
+ audio_embeds[audio_input_ids_mask.to(audio_embeds.device)]
+ if audio_input_ids_mask is not None
+ else audio_embeds
+ )
+ inputs_embeds = inputs_embeds.masked_scatter(
+ audio_token_mask[..., None].expand_as(inputs_embeds), audio_embeds.to(inputs_embeds.device)
+ )
+ elif audio_input_ids is not None:
+ inputs_embeds = audio_embeds
+
+ if use_cache and past_key_values is None:
+ past_key_values = DynamicCache(config=self.config)
+
+ if position_ids is None:
+ past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
+ position_ids = torch.arange(inputs_embeds.shape[1], device=inputs_embeds.device) + past_seen_tokens
+ position_ids = position_ids.unsqueeze(0)
+
+ causal_mask = create_causal_mask(
+ config=self.config,
+ inputs_embeds=inputs_embeds,
+ attention_mask=attention_mask,
+ past_key_values=past_key_values,
+ position_ids=position_ids,
+ )
+
+ hidden_states = inputs_embeds
+ position_embeddings = self.rotary_emb(hidden_states, position_ids)
+
+ for decoder_layer in self.layers[: self.config.num_hidden_layers]:
+ hidden_states = decoder_layer(
+ hidden_states,
+ attention_mask=causal_mask,
+ audio_token_mask=audio_token_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ position_embeddings=position_embeddings,
+ **kwargs,
+ )
+
+ hidden_states = self.norm(hidden_states)
+ return BaseModelOutputWithPast(
+ last_hidden_state=hidden_states,
+ past_key_values=past_key_values,
+ )
+
+
+@auto_docstring(
+ custom_intro="""
+ The Higgs Audio model, a llama-like auto-regressive transformer model with dual-FFN.
+ """
+)
+class HiggsAudioV2ForConditionalGeneration(HiggsAudioV2PreTrainedModel, HiggsAudioV2GenerationMixin):
+ base_model_prefix = "model"
+ _keys_to_ignore_on_load_unexpected = ["text_lm_head.weight"]
+
+ def __init__(self, config: HiggsAudioV2Config, use_text_head: bool = False):
+ r"""
+ use_text_head (`bool`, *optional*, defaults to False):
+ Whether to use a text language model head. Such head is not required for generation,
+ but can be used to compute the text loss when training.
+ """
+ super().__init__(config)
+ self.model = HiggsAudioV2Model(config)
+ self.audio_lm_head = nn.Linear(config.hidden_size, config.num_codebooks * config.codebook_size, bias=False)
+ self.text_lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False) if use_text_head else None
+
+ self.post_init()
+
+ def prepare_inputs_for_generation(
+ self,
+ input_ids: torch.LongTensor,
+ audio_input_ids: torch.LongTensor | None = None,
+ audio_input_ids_mask: torch.LongTensor | None = None,
+ **kwargs,
+ ):
+ model_inputs = super().prepare_inputs_for_generation(input_ids, **kwargs)
+
+ if audio_input_ids is not None and model_inputs.get("past_key_values") is not None:
+ current_cache_length = model_inputs.get("past_key_values").get_seq_length()
+ audio_token_mask = (input_ids == self.config.audio_token_id) | (
+ input_ids == self.config.audio_delay_token_id
+ )
+ in_cache_num_audio_input_ids = audio_token_mask[:, :current_cache_length].sum(dim=-1)
+
+ # already cached audio_input_ids should be masked
+ # this surmise that audio_input_ids are right padded!
+ valid_audio_input_ids = audio_input_ids_mask.cumsum(dim=-1) > in_cache_num_audio_input_ids[:, None]
+ audio_input_ids_mask = audio_input_ids_mask & valid_audio_input_ids
+
+ if audio_input_ids_mask is not None and (~audio_input_ids_mask[:, :-1]).all():
+ # in decoding mode, we only pass audio_input_ids
+ audio_input_ids = audio_input_ids[:, -1:, :].clone(memory_format=torch.contiguous_format)
+ model_inputs.pop("input_ids", None)
+ audio_input_ids_mask = None
+
+ model_inputs["audio_input_ids"] = audio_input_ids
+ model_inputs["audio_input_ids_mask"] = audio_input_ids_mask
+
+ return model_inputs
+
+ @auto_docstring
+ @can_return_tuple
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.BoolTensor | None = None,
+ audio_input_ids: torch.LongTensor | None = None,
+ audio_input_ids_mask: torch.LongTensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ audio_labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ **kwargs: Unpack[TransformersKwargs],
+ ):
+ r"""
+ audio_input_ids (`torch.LongTensor` of shape `(batch_size, num_audio_frames, num_codebooks)`, *optional*):
+ Indices of audio codebook tokens.
+
+ Indices can be obtained using [`HiggsAudioV2TokenizerModel.encode`].
+ audio_input_ids_mask (`torch.BoolTensor` of shape `(batch_size, num_audio_frames)`, *optional*):
+ Indicates which audio frames in `audio_input_ids` are valid.
+ audio_labels (`torch.LongTensor` of shape `(batch_size, num_audio_frames, num_codebooks)`, *optional*):
+ Labels for the audio codebook tokens for computing the masked language modeling loss. Indices should either be in `[0, ...,
+ config.codebook_size]. Token with indices set to `-100` are ignored (masked), the loss is only computed for the tokens with labels in `[0, ..., config.codebook_size]`.
+ Can be obtained using `output_labels=True` when calling [`HiggsAudioV2Processor`].
+
+ Returns:
+ [`~models.modeling_outputs.CausalLMOutputWithPast`]:
+ A [`~models.modeling_outputs.CausalLMOutputWithPast`] containing the logits, loss (if labels are provided),
+ and other outputs from the model.
+
+ Example:
+
+ ```python
+ >>> from transformers import AutoProcessor, HiggsAudioV2ForConditionalGeneration
+ >>> model_id = "eustlb/higgs-audio-v2-generation-3B-base"
+ >>> processor = AutoProcessor.from_pretrained(model_id, device_map="auto")
+ >>> model = HiggsAudioV2ForConditionalGeneration.from_pretrained(model_id, device_map="auto")
+ >>> conversation = [
+ ... {
+ ... "role": "system",
+ ... "content": [
+ ... {
+ ... "type": "text",
+ ... "text": "Generate audio following instruction."
+ ... }
+ ... ]
+ ... },
+ ... {
+ ... "role": "scene",
+ ... "content": [
+ ... {
+ ... "type": "text",
+ ... "text": "Audio is recorded from a quiet room."
+ ... }
+ ... ]
+ ... },
+ ... {
+ ... "role": "user",
+ ... "content": [
+ ... {
+ ... "type": "text",
+ ... "text": "It was the night before my birthday. Hooray! It's almost here! It may not be a holiday, but it's the best day of the year."
+ ... }
+ ... ]
+ ... },
+ ... {
+ ... "role": "assistant",
+ ... "content": [
+ ... {
+ ... "type": "audio",
+ ... "url": "https://huggingface.co/datasets/eustlb/dummy-audio-samples-higgs/resolve/main/belinda.wav"
+ ... }
+ ... ]
+ ... },
+ ... {
+ ... "role": "user",
+ ... "content": [
+ ... {
+ ... "type": "text",
+ ... "text": "The sun rises in the east and sets in the west. This simple fact has been observed by humans for thousands of years."
+ ... }
+ ... ]
+ ... }
+ ... ]
+ >>> inputs = processor.apply_chat_template(conversation, return_dict=True, tokenize=True, sampling_rate=24000, return_tensors="pt")
+ >>> inputs = inputs.to(model.device)
+ >>> outputs = model(**inputs)
+ ```
+ """
+ outputs = self.model(
+ input_ids=input_ids,
+ attention_mask=attention_mask,
+ audio_input_ids=audio_input_ids,
+ audio_input_ids_mask=audio_input_ids_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ hidden_states = outputs.last_hidden_state
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
+ logits = self.audio_lm_head(hidden_states[:, slice_indices, :])
+
+ loss = None
+ if audio_labels is not None:
+ audio_logits = logits.reshape(*logits.shape[:2], self.config.num_codebooks, self.config.codebook_size)
+ audio_labels_expanded = input_ids.new_ones((*input_ids.shape[:2], 8)) * -100
+ audio_token_mask = self.model.get_placeholder_mask(input_ids, inputs_embeds, audio_input_ids_mask)
+ audio_labels_expanded[audio_token_mask] = audio_labels[audio_input_ids_mask]
+
+ codebook_losses = []
+ for codebook_idx in range(self.config.num_codebooks):
+ codebook_logits = audio_logits[:, :, codebook_idx, :]
+ codebook_labels = audio_labels_expanded[:, :, codebook_idx]
+ codebook_losses.append(
+ self.loss_function(codebook_logits, codebook_labels, self.config.codebook_size, **kwargs)
+ )
+
+ loss = sum(codebook_losses)
+
+ if labels is not None:
+ if self.text_lm_head is not None:
+ text_logits = self.text_lm_head(hidden_states[:, slice_indices, :])
+ text_loss = self.loss_function(text_logits, labels, self.config.vocab_size, **kwargs)
+ loss = text_loss if loss is None else loss + text_loss
+ else:
+ logger.warning_once(
+ f"`labels` provided to {self.__class__.__name__} but `text_lm_head` is disabled. "
+ f"Text labels ignored. Set `use_text_head=True` in model init to enable text loss."
+ )
+
+ return CausalLMOutputWithPast(
+ loss=loss,
+ logits=logits,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ )
+
+
+__all__ = [
+ "HiggsAudioV2ForConditionalGeneration",
+ "HiggsAudioV2PreTrainedModel",
+ "HiggsAudioV2Model",
+ "HiggsAudioV2Config",
+]
diff --git a/third_party/transformers/src/transformers/models/higgs_audio_v2/processing_higgs_audio_v2.py b/third_party/transformers/src/transformers/models/higgs_audio_v2/processing_higgs_audio_v2.py
new file mode 100644
index 0000000000000000000000000000000000000000..3ef8700ecb9efedd709dc751907bbb155f951bda
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/higgs_audio_v2/processing_higgs_audio_v2.py
@@ -0,0 +1,366 @@
+# Copyright 2025 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import re
+from itertools import islice
+from pathlib import Path
+
+from ...audio_utils import AudioInput, make_list_of_audio
+from ...feature_extraction_utils import BatchFeature
+from ...processing_utils import ProcessingKwargs, ProcessorMixin, Unpack
+from ...tokenization_utils_base import PreTokenizedInput, TextInput
+from ...utils import is_soundfile_available, is_torch_available, logging
+
+
+if is_torch_available():
+ import torch
+ import torch.nn.functional as F
+
+
+if is_soundfile_available():
+ import soundfile as sf
+
+
+logger = logging.get_logger(__name__)
+
+
+class HiggsAudioV2ProcessorKwargs(ProcessingKwargs, total=False):
+ _defaults = {
+ "text_kwargs": {
+ "padding": True,
+ "padding_side": "left",
+ },
+ "audio_kwargs": {
+ "padding": False,
+ "sampling_rate": 24000,
+ },
+ }
+
+
+class HiggsAudioV2Processor(ProcessorMixin):
+ r"""
+ Constructs a Higgs Audio processor which wraps a [`DacFeatureExtractor`], a [`AutoTokenizer`],
+ and a [`HiggsAudioV2TokenizerModel`] into a single processor. It inherits, the audio feature extraction, tokenizer,
+ and audio encode/decode functionalities.
+ See [`~HiggsAudioV2Processor.__call__`] and [`~HiggsAudioV2Processor.decode`] for more information.
+
+ Args:
+ feature_extractor (`DacFeatureExtractor`):
+ An instance of [`DacFeatureExtractor`]. The feature extractor is a required input.
+ tokenizer (`AutoTokenizer`):
+ An instance of [`AutoTokenizer`]. The tokenizer is a required input.
+ audio_tokenizer (`HiggsAudioV2TokenizerModel`):
+ An instance of [`HiggsAudioV2TokenizerModel`]. The audio tokenizer is a required input.
+ chat_template (`str`, *optional*):
+ A template string for chat formatting when combining text and audio interactions.
+ audio_token (`str`, *optional*, defaults to `"<|AUDIO_OUT|>"`):
+ The token used to represent audio output in the text sequence.
+ audio_bos_token (`str`, *optional*, defaults to `"<|audio_out_bos|>"`):
+ The beginning-of-sequence token for audio output.
+ audio_eos_token (`str`, *optional*, defaults to `"<|audio_eos|>"`):
+ The end-of-sequence token for audio output.
+ audio_delay_token (`str`, *optional*, defaults to `"<|reserved_special_token_6|>"`):
+ The token used for audio delay pattern in multi-codebook generation.
+ audio_stream_bos_id (`int`, *optional*, defaults to 1024):
+ The ID for the beginning-of-stream token in audio sequences.
+ audio_stream_eos_id (`int`, *optional*, defaults to 1025):
+ The ID for the end-of-stream token in audio sequences.
+ """
+
+ feature_extractor_class = "DacFeatureExtractor"
+ tokenizer_class = "AutoTokenizer"
+ audio_tokenizer_class = "HiggsAudioV2TokenizerModel"
+
+ def __init__(
+ self,
+ feature_extractor,
+ tokenizer,
+ audio_tokenizer,
+ chat_template=None,
+ audio_token="<|AUDIO_OUT|>",
+ audio_bos_token="<|audio_out_bos|>",
+ audio_eos_token="<|audio_eos|>",
+ audio_delay_token="<|reserved_special_token_6|>",
+ audio_stream_bos_id=1024,
+ audio_stream_eos_id=1025,
+ ):
+ self.audio_token = tokenizer.audio_token if hasattr(tokenizer, "audio_token") else audio_token
+ self.audio_bos_token = tokenizer.audio_bos_token if hasattr(tokenizer, "audio_bos_token") else audio_bos_token
+ self.audio_eos_token = tokenizer.audio_eos_token if hasattr(tokenizer, "audio_eos_token") else audio_eos_token
+ self.audio_delay_token = (
+ tokenizer.audio_delay_token if hasattr(tokenizer, "audio_delay_token") else audio_delay_token
+ )
+ self.audio_token_id = tokenizer.convert_tokens_to_ids(self.audio_token)
+ self.audio_bos_token_id = tokenizer.convert_tokens_to_ids(self.audio_bos_token)
+ self.audio_eos_token_id = tokenizer.convert_tokens_to_ids(self.audio_eos_token)
+ self.audio_delay_token_id = tokenizer.convert_tokens_to_ids(self.audio_delay_token)
+ self.audio_stream_bos_id = audio_stream_bos_id
+ self.audio_stream_eos_id = audio_stream_eos_id
+
+ super().__init__(
+ feature_extractor,
+ tokenizer,
+ audio_tokenizer=audio_tokenizer,
+ chat_template=chat_template,
+ )
+
+ def get_audio_tokens(self, num_audio_tokens):
+ """
+ Returns the audio tokens for a given number of audio tokens.
+ """
+ num_codebooks = self.audio_tokenizer.config.num_quantizers
+ return self.audio_token * (num_audio_tokens - (num_codebooks - 1)) + self.audio_delay_token * (
+ num_codebooks - 1
+ )
+
+ def __call__(
+ self,
+ text: TextInput | PreTokenizedInput | list[TextInput] | list[PreTokenizedInput] | None = None,
+ audio: AudioInput | None = None,
+ output_labels: bool | None = False,
+ **kwargs: Unpack[HiggsAudioV2ProcessorKwargs],
+ ):
+ output_kwargs = self._merge_kwargs(
+ HiggsAudioV2ProcessorKwargs,
+ tokenizer_init_kwargs=self.tokenizer.init_kwargs,
+ **kwargs,
+ )
+
+ text_kwargs = output_kwargs["text_kwargs"]
+ audio_kwargs = output_kwargs["audio_kwargs"]
+ return_tensors = text_kwargs.get("return_tensors", None)
+ if return_tensors != "pt":
+ raise ValueError(f"{self.__class__.__name__} only supports `return_tensors='pt'`.")
+
+ if isinstance(text, str):
+ text = [text]
+ elif not (isinstance(text, (list, tuple)) and all(isinstance(t, str) for t in text)):
+ raise ValueError("Invalid input text. Please provide a string, or a list of strings")
+ n_audio_in_text = [t.count(self.audio_token) for t in text]
+
+ n_audio = 0
+ if audio is not None:
+ audio = make_list_of_audio(audio)
+ n_audio = len(audio)
+
+ if sum(n_audio_in_text) > 0 and n_audio != sum(n_audio_in_text):
+ if audio is None:
+ raise ValueError("No audio were provided, but there are audio tokens in the prompt")
+ else:
+ raise ValueError(
+ f"The number of audio tokens in each text ({n_audio_in_text}) should be the same as the "
+ f"number of provided audios ({n_audio})."
+ )
+ elif sum(n_audio_in_text) == 0 and n_audio > 0:
+ raise ValueError("Audio were provided, but there are no audio tokens in the prompt")
+
+ if audio is not None:
+ # tokenize audio
+ audio_input_ids_list = []
+ for audio_el in audio:
+ # TODO: @eustlb, this should be batched !!!
+ audio_inputs = self.feature_extractor(audio_el, **audio_kwargs)
+
+ # TODO: @eustlb, padding_mask should be supported...
+ audio_inputs.pop("padding_mask", None)
+ audio_inputs.to(self.audio_tokenizer.device)
+ audio_input_ids = self.audio_tokenizer.encode(**audio_inputs).audio_codes
+
+ # add audio eos and bos
+ bos_codes = audio_input_ids.new_full((*audio_input_ids.shape[:2], 1), self.audio_stream_bos_id)
+ eos_codes = audio_input_ids.new_full((*audio_input_ids.shape[:2], 1), self.audio_stream_eos_id)
+ audio_input_ids = torch.cat([bos_codes, audio_input_ids, eos_codes], dim=2)
+
+ audio_input_ids = self.build_delay_pattern(audio_input_ids)
+ audio_input_ids_list.append(audio_input_ids[0].transpose(0, 1))
+
+ # expand audio tokens in text
+ num_audio_tokens_iter = iter(len(audio_input_ids) for audio_input_ids in audio_input_ids_list)
+ for i in range(len(text)):
+ expanded = re.sub(
+ re.escape(self.audio_token), lambda _: self.get_audio_tokens(next(num_audio_tokens_iter)), text[i]
+ )
+ text[i] = expanded
+
+ # convert to nested list according to n_audio_in_text
+ # [audio_1, audio_2, ...] -> [[audio_1_1, audio_1_2, ...], [audio_2_1, audio_2_2, ...], ...]
+ audio_input_ids_iter = iter(audio_input_ids_list)
+ audio_input_ids_list = [list(islice(audio_input_ids_iter, l)) for l in n_audio_in_text]
+ audio_input_ids_list = [torch.cat(batch_el, dim=0) for batch_el in audio_input_ids_list]
+
+ # pad and stack
+ lenghts = [ids.shape[0] for ids in audio_input_ids_list]
+ max_length = max(lenghts)
+ audio_input_ids_list = [
+ F.pad(ids, (0, 0, 0, max_length - ids.shape[0]), value=self.audio_stream_eos_id)
+ for ids in audio_input_ids_list
+ ]
+ audio_input_ids = torch.stack(audio_input_ids_list, dim=0)
+ audio_input_ids_mask = torch.arange(max_length)[None, :] < torch.tensor(lenghts)[:, None]
+
+ # tokenize text
+ data = self.tokenizer(text, **text_kwargs)
+ if audio is not None:
+ data.update(
+ {
+ "audio_input_ids": audio_input_ids,
+ "audio_input_ids_mask": audio_input_ids_mask,
+ }
+ )
+
+ if output_labels:
+ labels = data["input_ids"].clone()
+ labels[labels == self.audio_token_id] = -100
+ labels[labels == self.tokenizer.pad_token_id] = -100
+ labels[labels == self.audio_bos_token_id] = -100
+ data["labels"] = labels
+
+ if audio is not None:
+ audio_labels = audio_input_ids.clone()
+ audio_labels[audio_labels == self.audio_stream_bos_id] = -100
+ audio_labels[audio_labels == self.audio_stream_eos_id] = -100
+ data.update({"audio_labels": audio_labels})
+
+ return BatchFeature(data=data, tensor_type="pt")
+
+ def batch_decode(self, audio_input_ids):
+ """
+ Decode a batch of audio token sequences into audio waveforms.
+
+ This method processes audio token sequences generated by the model, extracting the actual audio tokens
+ between the beginning-of-stream (BOS) and end-of-stream (EOS) markers, reverting the delay pattern
+ used during generation, and decoding them into audio waveforms using the audio tokenizer.
+
+ Args:
+ audio_input_ids (`torch.LongTensor`):
+ Shape `(batch_size, sequence_length, num_codebooks)`
+ The audio token sequences to decode. These should contain audio tokens with BOS and EOS markers
+ in a delay pattern format as generated by the model.
+
+ Returns:
+ `list[torch.Tensor]`: A list of decoded audio waveforms, one for each batch element. Each waveform
+ is a 1D tensor containing the audio samples.
+ """
+ # start idx should be the last sequence index of the audio bos tokens
+ audio_bos_token_idxs = (audio_input_ids == self.audio_stream_bos_id).all(-1).nonzero()
+ start_of_generation_idx = audio_bos_token_idxs[-1, -1].item()
+
+ audio_input_ids = audio_input_ids[:, start_of_generation_idx:]
+
+ # end idx for each batch idx should be the first sequence index of the audio eos tokens
+ audio_eos_token_idxs = (audio_input_ids == self.audio_stream_eos_id).all(-1).nonzero()
+ end_of_generation_idxs = [
+ audio_eos_token_idxs[audio_eos_token_idxs[:, 0] == batch_idx, 1].min().item()
+ if len(audio_eos_token_idxs[audio_eos_token_idxs[:, 0] == batch_idx]) > 0
+ else audio_input_ids.shape[1]
+ for batch_idx in range(audio_input_ids.shape[0])
+ ]
+
+ audios = []
+ with torch.no_grad():
+ # TODO: @eustlb, this should be batched !!!
+ for batch_idx in range(audio_input_ids.shape[0]):
+ audio_token_ids = audio_input_ids[batch_idx, 1 : end_of_generation_idxs[batch_idx]]
+ audio_token_ids = self.revert_delay_pattern(audio_token_ids).clip(0, self.audio_stream_bos_id - 1)
+ audio_i = (
+ self.audio_tokenizer.decode(audio_token_ids.transpose(0, 1).unsqueeze(0))
+ .audio_values.cpu()
+ .squeeze()
+ )
+ audios.append(audio_i)
+
+ return audios
+
+ def decode(self, audio_input_ids):
+ if audio_input_ids.shape[0] != 1:
+ raise ValueError(
+ f"Expecting a single output to be decoded but received {audio_input_ids.shape[0]} samples instead."
+ )
+
+ return self.batch_decode(audio_input_ids)[0]
+
+ def build_delay_pattern(self, input_ids):
+ bsz, num_codebooks, seq_len = input_ids.shape
+ new_seq_len = seq_len + num_codebooks - 1
+
+ # Create output tensor with delay pattern
+ output = torch.ones((bsz, num_codebooks, new_seq_len), dtype=torch.long, device=input_ids.device)
+
+ # Create masks for different regions
+ bos_mask = torch.tril(output, -1) > 0
+ eos_mask = torch.triu(output, seq_len) > 0
+ data_mask = ~(bos_mask | eos_mask)
+
+ # Fill the tensor
+ output[bos_mask] = self.audio_stream_bos_id
+ output[data_mask] = input_ids.reshape(-1)
+ output[eos_mask] = self.audio_stream_eos_id
+
+ return output
+
+ def revert_delay_pattern(self, input_ids):
+ seq_len, num_codebooks = input_ids.shape
+ # Extract diagonal slices from the delay pattern
+ slices = []
+ for i in range(num_codebooks):
+ end_idx = seq_len - num_codebooks + 1 + i
+ slices.append(input_ids[i:end_idx, i : i + 1])
+
+ return torch.cat(slices, dim=1)
+
+ # Copied from transformers.models.csm.processing_csm.CsmProcessor.save_audio with Csm->HiggsAudioV2
+ def save_audio(
+ self,
+ audio: AudioInput,
+ saving_path: str | Path | list[str | Path],
+ **kwargs: Unpack[HiggsAudioV2ProcessorKwargs],
+ ):
+ # TODO: @eustlb, this should be in AudioProcessor
+ if not is_soundfile_available():
+ raise ImportError("Please install `soundfile` to save audio files.")
+
+ # ensure correct audio input
+ audio = make_list_of_audio(audio)
+
+ # ensure correct saving path
+ if isinstance(saving_path, (str, Path)):
+ saving_path = [saving_path]
+ elif not (isinstance(saving_path, (list, tuple)) and all(isinstance(p, (str, Path)) for p in saving_path)):
+ raise ValueError("Invalid input path. Please provide a string, or a list of strings")
+
+ if len(audio) != len(saving_path):
+ raise ValueError("The number of audio and saving paths must be the same")
+
+ output_kwargs = self._merge_kwargs(
+ HiggsAudioV2ProcessorKwargs,
+ **kwargs,
+ )
+ audio_kwargs = output_kwargs["audio_kwargs"]
+ sampling_rate = audio_kwargs["sampling_rate"]
+
+ for audio_value, p in zip(audio, saving_path):
+ if isinstance(audio_value, torch.Tensor):
+ audio_value = audio_value.cpu().float().numpy()
+ sf.write(p, audio_value, sampling_rate)
+
+ @property
+ def model_input_names(self):
+ tokenizer_input_names = self.tokenizer.model_input_names
+
+ # TODO: @eustlb, to be standardized!!
+ audio_tokenizer_input_names = ["audio_input_ids", "audio_input_ids_mask"]
+ return tokenizer_input_names + audio_tokenizer_input_names
+
+
+__all__ = ["HiggsAudioV2Processor"]
diff --git a/third_party/transformers/src/transformers/models/hunyuan_v1_moe/__init__.py b/third_party/transformers/src/transformers/models/hunyuan_v1_moe/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..5be838ec745db0ad041804df14c883ea397739f8
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/hunyuan_v1_moe/__init__.py
@@ -0,0 +1,14 @@
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_hunyuan_v1_moe import *
+ from .modeling_hunyuan_v1_moe import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/hunyuan_v1_moe/configuration_hunyuan_v1_moe.py b/third_party/transformers/src/transformers/models/hunyuan_v1_moe/configuration_hunyuan_v1_moe.py
new file mode 100644
index 0000000000000000000000000000000000000000..27c6fb149aa4a924ce01fa18c7dd26b4703e022e
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/hunyuan_v1_moe/configuration_hunyuan_v1_moe.py
@@ -0,0 +1,102 @@
+# Copyright (C) 2025 THL A29 Limited, a Tencent company and the HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""HunYuanMoEV1 model configuration"""
+
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...modeling_rope_utils import RopeParameters
+from ...utils import auto_docstring
+
+
+@auto_docstring(checkpoint="tencent/Hunyuan-A13B-Instruct")
+@strict
+class HunYuanMoEV1Config(PreTrainedConfig):
+ r"""
+ eod_token_id (int, *optional*, defaults to 3):
+ Token ID representing the end-of-document marker. Used to indicate the termination of a text sequence.
+ For Example, in multi-document processing, this token helps the model distinguish between separate documents.
+ moe_topk (`int | list`, *optional*, defaults to 1):
+ Number of experts selected per token (Top-K routing). List form enables layer-wise customization.
+ """
+
+ model_type = "hunyuan_v1_moe"
+ keys_to_ignore_at_inference = ["past_key_values"]
+ attribute_map = {
+ "num_experts_per_tok": "moe_topk",
+ "num_local_experts": "num_experts",
+ }
+
+ vocab_size: int = 290943
+ hidden_size: int = 4096
+ intermediate_size: int = 11008
+ num_hidden_layers: int = 32
+ num_attention_heads: int = 32
+ num_key_value_heads: int | None = None
+ hidden_act: str = "silu"
+ max_position_embeddings: int = 2048
+ initializer_range: float = 0.02
+ rms_norm_eps: float = 1e-5
+ use_cache: bool = True
+ pad_token_id: int | None = 0
+ bos_token_id: int | None = 1
+ eos_token_id: int | list[int] | None = 2
+ eod_token_id: int | None = 3
+ sep_token_id: int | None = 4
+ pretraining_tp: int = 1
+ tie_word_embeddings: bool = False
+ rope_parameters: RopeParameters | dict | None = None
+ attention_bias: bool = False
+ attention_dropout: float | int = 0.0
+ num_experts: int = 1
+ moe_topk: int | list[int] = 1
+ head_dim: int | None = None
+
+ def __post_init__(self, **kwargs):
+ if self.num_key_value_heads is None:
+ self.num_key_value_heads = self.num_attention_heads
+ super().__post_init__(**kwargs)
+
+ def _rope_parameters_validation(self):
+ """
+ Validate the `rope_parameters` configuration.
+ """
+ if self.rope_parameters is None:
+ return
+
+ if not isinstance(self.rope_parameters, dict) or len(self.rope_parameters) != 2:
+ raise ValueError(
+ "`rope_parameters` must be a dictionary with two fields, `type` and `factor` or `type` and `alpha`,"
+ f"got {self.rope_parameters}"
+ )
+ rope_parameters_type = self.rope_parameters.get("type", None)
+ rope_parameters_factor = self.rope_parameters.get("factor", None)
+ rope_parameters_alpha = self.rope_parameters.get("alpha", None)
+ if rope_parameters_type is None or rope_parameters_type not in ["linear", "dynamic"]:
+ raise ValueError(
+ f"`rope_parameters`'s type field must be one of ['linear', 'dynamic'], got {rope_parameters_type}"
+ )
+ if rope_parameters_factor is None and rope_parameters_alpha is None:
+ raise ValueError("`rope_parameters`'s factor or alpha field must be have one, got both of none")
+ if rope_parameters_factor is not None:
+ if not isinstance(rope_parameters_factor, float) or rope_parameters_factor <= 1.0:
+ raise ValueError(
+ f"`rope_parameters`'s factor field must be a float > 1.0, got {rope_parameters_factor}"
+ )
+ if rope_parameters_alpha is not None:
+ if not isinstance(rope_parameters_alpha, float) or rope_parameters_alpha <= 1.0:
+ raise ValueError(f"`rope_parameters`'s alpha field must be a float > 1.0, got {rope_parameters_alpha}")
+
+
+__all__ = ["HunYuanMoEV1Config"]
diff --git a/third_party/transformers/src/transformers/models/hunyuan_v1_moe/modeling_hunyuan_v1_moe.py b/third_party/transformers/src/transformers/models/hunyuan_v1_moe/modeling_hunyuan_v1_moe.py
new file mode 100644
index 0000000000000000000000000000000000000000..19779da0528ca2434557078701b8be0d488fe521
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/hunyuan_v1_moe/modeling_hunyuan_v1_moe.py
@@ -0,0 +1,633 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/hunyuan_v1_moe/modular_hunyuan_v1_moe.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_hunyuan_v1_moe.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright (C) 2025 THL A29 Limited, a Tencent company and the HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from collections.abc import Callable
+from typing import Optional
+
+import torch
+import torch.nn.functional as F
+from torch import nn
+
+from ... import initialization as init
+from ...activations import ACT2FN
+from ...cache_utils import Cache, DynamicCache
+from ...generation import GenerationMixin
+from ...integrations import (
+ use_experts_implementation,
+ use_kernel_forward_from_hub,
+ use_kernel_func_from_hub,
+ use_kernelized_func,
+)
+from ...masking_utils import create_causal_mask
+from ...modeling_layers import GenericForSequenceClassification, GradientCheckpointingLayer
+from ...modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast
+from ...modeling_rope_utils import ROPE_INIT_FUNCTIONS, dynamic_rope_update
+from ...modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
+from ...processing_utils import Unpack
+from ...utils import TransformersKwargs, auto_docstring, can_return_tuple
+from ...utils.generic import maybe_autocast, merge_with_config_defaults
+from ...utils.output_capturing import capture_outputs
+from .configuration_hunyuan_v1_moe import HunYuanMoEV1Config
+
+
+@use_kernel_forward_from_hub("RMSNorm")
+class HunYuanMoEV1RMSNorm(nn.Module):
+ def __init__(self, hidden_size, eps: float = 1e-6) -> None:
+ """
+ HunYuanMoEV1RMSNorm is equivalent to T5LayerNorm
+ """
+ super().__init__()
+ self.weight = nn.Parameter(torch.ones(hidden_size))
+ self.variance_epsilon = eps
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ input_dtype = hidden_states.dtype
+ hidden_states = hidden_states.to(torch.float32)
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
+ hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
+ return self.weight * hidden_states.to(input_dtype)
+
+ def extra_repr(self):
+ return f"{tuple(self.weight.shape)}, eps={self.variance_epsilon}"
+
+
+class HunYuanMoEV1MLP(nn.Module):
+ def __init__(self, config: HunYuanMoEV1Config):
+ super().__init__()
+ self.config = config
+ self.hidden_size = config.hidden_size
+ self.intermediate_size = config.intermediate_size
+ self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
+ self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
+ self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False)
+ self.act_fn = ACT2FN[config.hidden_act]
+
+ def forward(self, x):
+ down_proj = self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
+ return down_proj
+
+
+def rotate_half(x):
+ """Rotates half the hidden dims of the input."""
+ x1 = x[..., : x.shape[-1] // 2]
+ x2 = x[..., x.shape[-1] // 2 :]
+ return torch.cat((-x2, x1), dim=-1)
+
+
+@use_kernel_func_from_hub("rotary_pos_emb")
+def apply_rotary_pos_emb(q, k, cos, sin, unsqueeze_dim=1):
+ """Applies Rotary Position Embedding to the query and key tensors.
+
+ Args:
+ q (`torch.Tensor`): The query tensor.
+ k (`torch.Tensor`): The key tensor.
+ cos (`torch.Tensor`): The cosine part of the rotary embedding.
+ sin (`torch.Tensor`): The sine part of the rotary embedding.
+ unsqueeze_dim (`int`, *optional*, defaults to 1):
+ The 'unsqueeze_dim' argument specifies the dimension along which to unsqueeze cos[position_ids] and
+ sin[position_ids] so that they can be properly broadcasted to the dimensions of q and k. For example, note
+ that cos[position_ids] and sin[position_ids] have the shape [batch_size, seq_len, head_dim]. Then, if q and
+ k have the shape [batch_size, heads, seq_len, head_dim], then setting unsqueeze_dim=1 makes
+ cos[position_ids] and sin[position_ids] broadcastable to the shapes of q and k. Similarly, if q and k have
+ the shape [batch_size, seq_len, heads, head_dim], then set unsqueeze_dim=2.
+ Returns:
+ `tuple(torch.Tensor)` comprising of the query and key tensors rotated using the Rotary Position Embedding.
+ """
+ cos = cos.unsqueeze(unsqueeze_dim)
+ sin = sin.unsqueeze(unsqueeze_dim)
+ q_embed = (q * cos) + (rotate_half(q) * sin)
+ k_embed = (k * cos) + (rotate_half(k) * sin)
+ return q_embed, k_embed
+
+
+def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
+ """
+ This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
+ num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
+ """
+ batch, num_key_value_heads, slen, head_dim = hidden_states.shape
+ if n_rep == 1:
+ return hidden_states
+ hidden_states = hidden_states[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim)
+ return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
+
+
+def eager_attention_forward(
+ module: nn.Module,
+ query: torch.Tensor,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ attention_mask: torch.Tensor | None,
+ scaling: float,
+ dropout: float = 0.0,
+ **kwargs: Unpack[TransformersKwargs],
+):
+ key_states = repeat_kv(key, module.num_key_value_groups)
+ value_states = repeat_kv(value, module.num_key_value_groups)
+
+ attn_weights = torch.matmul(query, key_states.transpose(2, 3)) * scaling
+ if attention_mask is not None:
+ attn_weights = attn_weights + attention_mask
+
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query.dtype)
+ attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
+ attn_output = torch.matmul(attn_weights, value_states)
+ attn_output = attn_output.transpose(1, 2).contiguous()
+
+ return attn_output, attn_weights
+
+
+@use_kernelized_func(apply_rotary_pos_emb)
+class HunYuanMoEV1Attention(nn.Module):
+ """Multi-headed attention from 'Attention Is All You Need' paper"""
+
+ def __init__(self, config: HunYuanMoEV1Config, layer_idx: int):
+ super().__init__()
+ self.config = config
+ self.layer_idx = layer_idx
+ self.head_dim = getattr(config, "head_dim", config.hidden_size // config.num_attention_heads)
+ self.num_key_value_groups = config.num_attention_heads // config.num_key_value_heads
+ self.scaling = self.head_dim**-0.5
+ self.attention_dropout = config.attention_dropout
+ self.is_causal = True
+
+ self.q_proj = nn.Linear(
+ config.hidden_size, config.num_attention_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.k_proj = nn.Linear(
+ config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.v_proj = nn.Linear(
+ config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.o_proj = nn.Linear(
+ config.num_attention_heads * self.head_dim, config.hidden_size, bias=config.attention_bias
+ )
+ self.query_layernorm = HunYuanMoEV1RMSNorm(self.head_dim, eps=config.rms_norm_eps)
+ self.key_layernorm = HunYuanMoEV1RMSNorm(self.head_dim, eps=config.rms_norm_eps)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor],
+ attention_mask: torch.Tensor | None,
+ past_key_values: Cache | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple[torch.Tensor, torch.Tensor]:
+ input_shape = hidden_states.shape[:-1]
+ hidden_shape = (*input_shape, -1, self.head_dim)
+
+ query_states = self.q_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+ key_states = self.k_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+ value_states = self.v_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+
+ cos, sin = position_embeddings
+ query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin)
+ query_states = self.query_layernorm(query_states)
+ key_states = self.key_layernorm(key_states)
+
+ if past_key_values is not None:
+ key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx)
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ query_states,
+ key_states,
+ value_states,
+ attention_mask,
+ dropout=0.0 if not self.training else self.attention_dropout,
+ scaling=self.scaling,
+ **kwargs,
+ )
+
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
+ attn_output = self.o_proj(attn_output)
+ return attn_output, attn_weights
+
+
+class HunYuanMoEV1Gate(nn.Module):
+ def __init__(self, config: HunYuanMoEV1Config, layer_idx: int | None = None):
+ super().__init__()
+ self.config = config
+ self.layer_idx = layer_idx
+ num_experts = config.num_experts if isinstance(config.num_experts, int) else config.num_experts[layer_idx]
+ self.wg = nn.Linear(config.hidden_size, num_experts, bias=False, dtype=torch.float32)
+
+ def forward(self, hidden_states):
+ bsz, seq_len, hidden_size = hidden_states.shape
+ hidden_states = hidden_states.reshape(-1, hidden_size)
+ if self.wg.weight.dtype == torch.float32:
+ hidden_states = hidden_states.float()
+ logits = self.wg(hidden_states)
+ return logits
+
+
+@use_experts_implementation
+class HunYuanMoEV1Experts(nn.Module):
+ """Collection of expert weights stored as 3D tensors."""
+
+ def __init__(self, config: HunYuanMoEV1Config):
+ super().__init__()
+ self.num_experts = config.num_local_experts
+ self.hidden_dim = config.hidden_size
+ self.intermediate_dim = config.intermediate_size
+ self.gate_up_proj = nn.Parameter(torch.empty(self.num_experts, 2 * self.intermediate_dim, self.hidden_dim))
+ self.down_proj = nn.Parameter(torch.empty(self.num_experts, self.hidden_dim, self.intermediate_dim))
+ self.act_fn = ACT2FN[config.hidden_act]
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ top_k_index: torch.Tensor,
+ top_k_weights: torch.Tensor,
+ ) -> torch.Tensor:
+ final_hidden_states = torch.zeros_like(hidden_states)
+ with torch.no_grad():
+ expert_mask = torch.nn.functional.one_hot(top_k_index, num_classes=self.num_experts)
+ expert_mask = expert_mask.permute(2, 1, 0)
+ expert_hit = torch.greater(expert_mask.sum(dim=(-1, -2)), 0).nonzero()
+
+ for expert_idx in expert_hit:
+ expert_idx = expert_idx[0]
+ if expert_idx == self.num_experts:
+ continue
+ top_k_pos, token_idx = torch.where(expert_mask[expert_idx])
+ current_state = hidden_states[token_idx]
+ gate, up = nn.functional.linear(current_state, self.gate_up_proj[expert_idx]).chunk(2, dim=-1)
+ current_hidden_states = self.act_fn(gate) * up
+ current_hidden_states = nn.functional.linear(current_hidden_states, self.down_proj[expert_idx])
+ current_hidden_states = current_hidden_states * top_k_weights[token_idx, top_k_pos, None]
+ final_hidden_states.index_add_(0, token_idx, current_hidden_states.to(final_hidden_states.dtype))
+
+ return final_hidden_states
+
+
+class HunYuanMoEV1Moe(nn.Module):
+ def __init__(self, config: HunYuanMoEV1Config, layer_idx: int | None = None):
+ super().__init__()
+ self.config = config
+ self.layer_idx = layer_idx
+ self.num_experts = config.num_experts if isinstance(config.num_experts, int) else config.num_experts[layer_idx]
+ self.top_k = config.moe_topk if isinstance(config.moe_topk, int) else config.moe_topk[layer_idx]
+ self.gate = HunYuanMoEV1Gate(config, layer_idx=layer_idx)
+ self.experts = HunYuanMoEV1Experts(config)
+ self.shared_mlp = HunYuanMoEV1MLP(config)
+
+ def route_tokens_to_experts(self, hidden_states):
+ routing_weights = F.softmax(hidden_states, dim=1, dtype=torch.float)
+ routing_weights, selected_experts = torch.topk(routing_weights, self.top_k, dim=-1)
+ routing_weights /= routing_weights.sum(dim=-1, keepdim=True)
+ return selected_experts, routing_weights.to(hidden_states.dtype)
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ batch_size, sequence_length, hidden_dim = hidden_states.shape
+ hidden_states_mlp = self.shared_mlp(hidden_states)
+ router_logits = self.gate(hidden_states)
+ hidden_states = hidden_states.view(-1, hidden_dim)
+ selected_experts, routing_weights = self.route_tokens_to_experts(router_logits)
+ final_hidden_states = self.experts(hidden_states, selected_experts, routing_weights).reshape(
+ batch_size, sequence_length, hidden_dim
+ )
+ return final_hidden_states + hidden_states_mlp
+
+
+class HunYuanMoEV1DecoderLayer(GradientCheckpointingLayer):
+ def __init__(self, config: HunYuanMoEV1Config, layer_idx: int):
+ super().__init__()
+ self.hidden_size = config.hidden_size
+ self.self_attn = HunYuanMoEV1Attention(config=config, layer_idx=layer_idx)
+ self.mlp = HunYuanMoEV1Moe(config, layer_idx=layer_idx)
+ self.input_layernorm = HunYuanMoEV1RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.post_attention_layernorm = HunYuanMoEV1RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.layer_idx = layer_idx
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ use_cache: bool | None = False,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> torch.Tensor:
+ residual = hidden_states
+ hidden_states = self.input_layernorm(hidden_states)
+ # Self Attention
+ hidden_states, _ = self.self_attn(
+ hidden_states=hidden_states,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ position_embeddings=position_embeddings,
+ **kwargs,
+ )
+ hidden_states = residual + hidden_states
+
+ # Fully Connected
+ residual = hidden_states
+ hidden_states = self.post_attention_layernorm(hidden_states)
+ hidden_states = self.mlp(hidden_states)
+ hidden_states = residual + hidden_states
+ return hidden_states
+
+
+@auto_docstring
+class HunYuanMoEV1PreTrainedModel(PreTrainedModel):
+ config: HunYuanMoEV1Config
+ base_model_prefix = "model"
+ supports_gradient_checkpointing = True
+ _no_split_modules = ["HunYuanMoEV1DecoderLayer"]
+ _skip_keys_device_placement = ["past_key_values"]
+ _supports_flash_attn = True
+ _supports_sdpa = True
+ _supports_flex_attn = True
+
+ _can_compile_fullgraph = True
+ _supports_attention_backend = True
+ _can_record_outputs = {
+ "hidden_states": HunYuanMoEV1DecoderLayer,
+ "attentions": HunYuanMoEV1Attention,
+ }
+
+ @torch.no_grad()
+ def _init_weights(self, module):
+ super()._init_weights(module)
+ if isinstance(module, HunYuanMoEV1Experts):
+ init.normal_(module.gate_up_proj, mean=0.0, std=self.config.initializer_range)
+ init.normal_(module.down_proj, mean=0.0, std=self.config.initializer_range)
+ # DynamicNTKAlphaRotary - unique to this model
+ elif "RotaryEmbedding" in module.__class__.__name__ and hasattr(module, "original_inv_freq"):
+ if module.rope_type == "dynamic" and module.config.rope_parameters.get("alpha"):
+ dim = module.config.head_dim
+ rope_theta = module.config.rope_parameters["rope_theta"]
+ alpha = module.config.rope_parameters["alpha"]
+
+ base = rope_theta * alpha ** (dim / (dim - 2))
+ buffer_value = 1.0 / (base ** (torch.arange(0, dim, 2).float() / dim))
+ else:
+ rope_fn = (
+ ROPE_INIT_FUNCTIONS[module.rope_type]
+ if module.rope_type != "default"
+ else module.compute_default_rope_parameters
+ )
+ buffer_value, _ = rope_fn(module.config)
+ init.copy_(module.inv_freq, buffer_value)
+ init.copy_(module.original_inv_freq, buffer_value)
+
+
+class HunYuanMoEV1RotaryEmbedding(nn.Module):
+ inv_freq: torch.Tensor # fix linting for `register_buffer`
+
+ def __init__(self, config: HunYuanMoEV1Config, device=None):
+ super().__init__()
+ self.max_seq_len_cached = config.max_position_embeddings
+ self.original_max_seq_len = config.max_position_embeddings
+
+ self.config = config
+
+ self.rope_type = self.config.rope_parameters["rope_type"]
+
+ # Diff from Llama - DynamicNTKAlphaRotary
+ if self.rope_type == "dynamic" and self.config.rope_parameters.get("alpha"):
+ self.dim = config.head_dim
+ base = self.config.rope_parameters["rope_theta"] * self.config.rope_parameters["alpha"] ** (
+ self.config.head_dim / (self.config.head_dim - 2)
+ )
+ inv_freq = 1.0 / (base ** (torch.arange(0, self.dim, 2).float().to(device) / self.config.head_dim))
+ self.attention_scaling = 1.0
+ else:
+ rope_init_fn: Callable = self.compute_default_rope_parameters
+ if self.rope_type != "default":
+ rope_init_fn = ROPE_INIT_FUNCTIONS[self.rope_type]
+ inv_freq, self.attention_scaling = rope_init_fn(self.config, device)
+
+ self.register_buffer("inv_freq", inv_freq, persistent=False)
+ self.register_buffer("original_inv_freq", inv_freq.clone(), persistent=False)
+
+ @staticmethod
+ def compute_default_rope_parameters(
+ config: HunYuanMoEV1Config | None = None,
+ device: Optional["torch.device"] = None,
+ seq_len: int | None = None,
+ ) -> tuple["torch.Tensor", float]:
+ """
+ Computes the inverse frequencies according to the original RoPE implementation
+ Args:
+ config ([`~transformers.PreTrainedConfig`]):
+ The model configuration.
+ device (`torch.device`):
+ The device to use for initialization of the inverse frequencies.
+ seq_len (`int`, *optional*):
+ The current sequence length. Unused for this type of RoPE.
+ Returns:
+ Tuple of (`torch.Tensor`, `float`), containing the inverse frequencies for the RoPE embeddings and the
+ post-processing scaling factor applied to the computed cos/sin (unused in this type of RoPE).
+ """
+ base = config.rope_parameters["rope_theta"]
+ dim = getattr(config, "head_dim", None) or config.hidden_size // config.num_attention_heads
+
+ attention_factor = 1.0 # Unused in this type of RoPE
+
+ # Compute the inverse frequencies
+ inv_freq = 1.0 / (
+ base ** (torch.arange(0, dim, 2, dtype=torch.int64).to(device=device, dtype=torch.float) / dim)
+ )
+ return inv_freq, attention_factor
+
+ @torch.no_grad()
+ @dynamic_rope_update # power user: used with advanced RoPE types (e.g. dynamic rope)
+ def forward(self, x, position_ids):
+ inv_freq_expanded = self.inv_freq[None, :, None].float().expand(position_ids.shape[0], -1, 1).to(x.device)
+ position_ids_expanded = position_ids[:, None, :].float()
+
+ device_type = x.device.type if isinstance(x.device.type, str) and x.device.type != "mps" else "cpu"
+ with maybe_autocast(device_type=device_type, enabled=False): # Force float32
+ freqs = (inv_freq_expanded.float() @ position_ids_expanded.float()).transpose(1, 2)
+ emb = torch.cat((freqs, freqs), dim=-1)
+ cos = emb.cos() * self.attention_scaling
+ sin = emb.sin() * self.attention_scaling
+
+ return cos.to(dtype=x.dtype), sin.to(dtype=x.dtype)
+
+
+@auto_docstring
+class HunYuanMoEV1Model(HunYuanMoEV1PreTrainedModel):
+ def __init__(self, config: HunYuanMoEV1Config):
+ super().__init__(config)
+ self.padding_idx = config.pad_token_id
+ self.vocab_size = config.vocab_size
+
+ self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)
+ self.layers = nn.ModuleList(
+ [HunYuanMoEV1DecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
+ )
+ self.norm = HunYuanMoEV1RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.rotary_emb = HunYuanMoEV1RotaryEmbedding(config=config)
+ self.gradient_checkpointing = False
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @merge_with_config_defaults
+ @capture_outputs
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ use_cache: bool | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> BaseModelOutputWithPast:
+ 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 None:
+ inputs_embeds: torch.Tensor = self.embed_tokens(input_ids)
+
+ if use_cache and past_key_values is None:
+ past_key_values = DynamicCache(config=self.config)
+
+ if position_ids is None:
+ past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
+ position_ids = torch.arange(inputs_embeds.shape[1], device=inputs_embeds.device) + past_seen_tokens
+ position_ids = position_ids.unsqueeze(0)
+
+ causal_mask = create_causal_mask(
+ config=self.config,
+ inputs_embeds=inputs_embeds,
+ attention_mask=attention_mask,
+ past_key_values=past_key_values,
+ position_ids=position_ids,
+ )
+
+ hidden_states = inputs_embeds
+ position_embeddings = self.rotary_emb(hidden_states, position_ids=position_ids)
+
+ for decoder_layer in self.layers[: self.config.num_hidden_layers]:
+ hidden_states = decoder_layer(
+ hidden_states,
+ attention_mask=causal_mask,
+ position_embeddings=position_embeddings,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ hidden_states = self.norm(hidden_states)
+ return BaseModelOutputWithPast(
+ last_hidden_state=hidden_states,
+ past_key_values=past_key_values,
+ )
+
+
+@auto_docstring
+class HunYuanMoEV1ForCausalLM(HunYuanMoEV1PreTrainedModel, GenerationMixin):
+ _tied_weights_keys = {"lm_head.weight": "model.embed_tokens.weight"}
+ _tp_plan = {"lm_head": "colwise_gather_output"}
+ _pp_plan = {"lm_head": (["hidden_states"], ["logits"])}
+
+ def __init__(self, config):
+ super().__init__(config)
+ self.model = HunYuanMoEV1Model(config)
+ self.vocab_size = config.vocab_size
+ self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> CausalLMOutputWithPast:
+ r"""
+ Example:
+
+ ```python
+ >>> from transformers import AutoTokenizer, HunYuanMoEV1ForCausalLM
+
+ >>> model = HunYuanMoEV1ForCausalLM.from_pretrained("meta-hunyuan_v1_moe/HunYuanMoEV1-2-7b-hf")
+ >>> tokenizer = AutoTokenizer.from_pretrained("meta-hunyuan_v1_moe/HunYuanMoEV1-2-7b-hf")
+
+ >>> prompt = "Hey, are you conscious? Can you talk to me?"
+ >>> inputs = tokenizer(prompt, return_tensors="pt")
+
+ >>> # Generate
+ >>> generate_ids = model.generate(inputs.input_ids, max_length=30)
+ >>> tokenizer.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]
+ "Hey, are you conscious? Can you talk to me?\nI'm not conscious, but I can talk to you."
+ ```"""
+ outputs: BaseModelOutputWithPast = self.model(
+ input_ids=input_ids,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ hidden_states = outputs.last_hidden_state
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(logits=logits, labels=labels, vocab_size=self.config.vocab_size, **kwargs)
+
+ return CausalLMOutputWithPast(
+ loss=loss,
+ logits=logits,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ )
+
+
+class HunYuanMoEV1ForSequenceClassification(GenericForSequenceClassification, HunYuanMoEV1PreTrainedModel):
+ pass
+
+
+__all__ = [
+ "HunYuanMoEV1ForCausalLM",
+ "HunYuanMoEV1Model",
+ "HunYuanMoEV1PreTrainedModel",
+ "HunYuanMoEV1ForSequenceClassification",
+]
diff --git a/third_party/transformers/src/transformers/models/hunyuan_v1_moe/modular_hunyuan_v1_moe.py b/third_party/transformers/src/transformers/models/hunyuan_v1_moe/modular_hunyuan_v1_moe.py
new file mode 100644
index 0000000000000000000000000000000000000000..2a60c26bc39117df2f237effbc84c76a50e9689b
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/hunyuan_v1_moe/modular_hunyuan_v1_moe.py
@@ -0,0 +1,218 @@
+# Copyright (C) 2025 THL A29 Limited, a Tencent company and the HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""PyTorch HunYuanMoEV1 model."""
+
+from collections.abc import Callable
+
+import torch
+import torch.nn.functional as F
+from torch import nn
+
+from ... import initialization as init
+from ...cache_utils import Cache
+from ...modeling_rope_utils import ROPE_INIT_FUNCTIONS
+from ...modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
+from ...processing_utils import Unpack
+from ...utils import TransformersKwargs, logging
+from ..hunyuan_v1_dense.modeling_hunyuan_v1_dense import HunYuanDenseV1RotaryEmbedding
+from ..llama.modeling_llama import (
+ LlamaAttention,
+ LlamaDecoderLayer,
+ LlamaForCausalLM,
+ LlamaForSequenceClassification,
+ LlamaMLP,
+ LlamaModel,
+ LlamaPreTrainedModel,
+ LlamaRMSNorm,
+ apply_rotary_pos_emb,
+ eager_attention_forward,
+)
+from ..mixtral.modeling_mixtral import MixtralExperts
+from .configuration_hunyuan_v1_moe import HunYuanMoEV1Config
+
+
+logger = logging.get_logger(__name__)
+
+
+class HunYuanMoEV1RMSNorm(LlamaRMSNorm):
+ pass
+
+
+class HunYuanMoEV1MLP(LlamaMLP):
+ def __init__(self, config: HunYuanMoEV1Config):
+ super().__init__(config)
+ self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
+ self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
+ self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False)
+
+
+class HunYuanMoEV1Attention(LlamaAttention):
+ def __init__(self, config: HunYuanMoEV1Config, layer_idx: int):
+ super().__init__(config, layer_idx)
+ self.query_layernorm = HunYuanMoEV1RMSNorm(self.head_dim, eps=config.rms_norm_eps)
+ self.key_layernorm = HunYuanMoEV1RMSNorm(self.head_dim, eps=config.rms_norm_eps)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor],
+ attention_mask: torch.Tensor | None,
+ past_key_values: Cache | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple[torch.Tensor, torch.Tensor]:
+ input_shape = hidden_states.shape[:-1]
+ hidden_shape = (*input_shape, -1, self.head_dim)
+
+ query_states = self.q_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+ key_states = self.k_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+ value_states = self.v_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+
+ cos, sin = position_embeddings
+ query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin)
+ query_states = self.query_layernorm(query_states)
+ key_states = self.key_layernorm(key_states)
+
+ if past_key_values is not None:
+ key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx)
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ query_states,
+ key_states,
+ value_states,
+ attention_mask,
+ dropout=0.0 if not self.training else self.attention_dropout,
+ scaling=self.scaling,
+ **kwargs,
+ )
+
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
+ attn_output = self.o_proj(attn_output)
+ return attn_output, attn_weights
+
+
+class HunYuanMoEV1Gate(nn.Module):
+ def __init__(self, config: HunYuanMoEV1Config, layer_idx: int | None = None):
+ super().__init__()
+ self.config = config
+ self.layer_idx = layer_idx
+ num_experts = config.num_experts if isinstance(config.num_experts, int) else config.num_experts[layer_idx]
+ self.wg = nn.Linear(config.hidden_size, num_experts, bias=False, dtype=torch.float32)
+
+ def forward(self, hidden_states):
+ bsz, seq_len, hidden_size = hidden_states.shape
+ hidden_states = hidden_states.reshape(-1, hidden_size)
+ if self.wg.weight.dtype == torch.float32:
+ hidden_states = hidden_states.float()
+ logits = self.wg(hidden_states)
+ return logits
+
+
+class HunYuanMoEV1Experts(MixtralExperts):
+ pass
+
+
+class HunYuanMoEV1Moe(nn.Module):
+ def __init__(self, config: HunYuanMoEV1Config, layer_idx: int | None = None):
+ super().__init__()
+ self.config = config
+ self.layer_idx = layer_idx
+ self.num_experts = config.num_experts if isinstance(config.num_experts, int) else config.num_experts[layer_idx]
+ self.top_k = config.moe_topk if isinstance(config.moe_topk, int) else config.moe_topk[layer_idx]
+ self.gate = HunYuanMoEV1Gate(config, layer_idx=layer_idx)
+ self.experts = HunYuanMoEV1Experts(config)
+ self.shared_mlp = HunYuanMoEV1MLP(config)
+
+ def route_tokens_to_experts(self, hidden_states):
+ routing_weights = F.softmax(hidden_states, dim=1, dtype=torch.float)
+ routing_weights, selected_experts = torch.topk(routing_weights, self.top_k, dim=-1)
+ routing_weights /= routing_weights.sum(dim=-1, keepdim=True)
+ return selected_experts, routing_weights.to(hidden_states.dtype)
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ batch_size, sequence_length, hidden_dim = hidden_states.shape
+ hidden_states_mlp = self.shared_mlp(hidden_states)
+ router_logits = self.gate(hidden_states)
+ hidden_states = hidden_states.view(-1, hidden_dim)
+ selected_experts, routing_weights = self.route_tokens_to_experts(router_logits)
+ final_hidden_states = self.experts(hidden_states, selected_experts, routing_weights).reshape(
+ batch_size, sequence_length, hidden_dim
+ )
+ return final_hidden_states + hidden_states_mlp
+
+
+class HunYuanMoEV1DecoderLayer(LlamaDecoderLayer):
+ def __init__(self, config: HunYuanMoEV1Config, layer_idx: int):
+ super().__init__(config, layer_idx)
+ self.hidden_size = config.hidden_size
+ self.self_attn = HunYuanMoEV1Attention(config=config, layer_idx=layer_idx)
+ self.mlp = HunYuanMoEV1Moe(config, layer_idx=layer_idx)
+ self.input_layernorm = HunYuanMoEV1RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.post_attention_layernorm = HunYuanMoEV1RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.layer_idx = layer_idx
+
+
+class HunYuanMoEV1PreTrainedModel(LlamaPreTrainedModel):
+ @torch.no_grad()
+ def _init_weights(self, module):
+ PreTrainedModel._init_weights(self, module)
+ if isinstance(module, HunYuanMoEV1Experts):
+ init.normal_(module.gate_up_proj, mean=0.0, std=self.config.initializer_range)
+ init.normal_(module.down_proj, mean=0.0, std=self.config.initializer_range)
+ # DynamicNTKAlphaRotary - unique to this model
+ elif "RotaryEmbedding" in module.__class__.__name__ and hasattr(module, "original_inv_freq"):
+ if module.rope_type == "dynamic" and module.config.rope_parameters.get("alpha"):
+ dim = module.config.head_dim
+ rope_theta = module.config.rope_parameters["rope_theta"]
+ alpha = module.config.rope_parameters["alpha"]
+
+ base = rope_theta * alpha ** (dim / (dim - 2))
+ buffer_value = 1.0 / (base ** (torch.arange(0, dim, 2).float() / dim))
+ else:
+ rope_fn = (
+ ROPE_INIT_FUNCTIONS[module.rope_type]
+ if module.rope_type != "default"
+ else module.compute_default_rope_parameters
+ )
+ buffer_value, _ = rope_fn(module.config)
+ init.copy_(module.inv_freq, buffer_value)
+ init.copy_(module.original_inv_freq, buffer_value)
+
+
+class HunYuanMoEV1RotaryEmbedding(HunYuanDenseV1RotaryEmbedding):
+ pass
+
+
+class HunYuanMoEV1Model(LlamaModel):
+ pass
+
+
+class HunYuanMoEV1ForCausalLM(LlamaForCausalLM):
+ pass
+
+
+class HunYuanMoEV1ForSequenceClassification(LlamaForSequenceClassification):
+ pass
+
+
+__all__ = [
+ "HunYuanMoEV1ForCausalLM",
+ "HunYuanMoEV1Model",
+ "HunYuanMoEV1PreTrainedModel",
+ "HunYuanMoEV1ForSequenceClassification",
+]
diff --git a/third_party/transformers/src/transformers/models/idefics3/__init__.py b/third_party/transformers/src/transformers/models/idefics3/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..a47ddba411002b0d12453c0ec6e8611a7968e921
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/idefics3/__init__.py
@@ -0,0 +1,30 @@
+# Copyright 2024 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_idefics3 import *
+ from .image_processing_idefics3 import *
+ from .image_processing_pil_idefics3 import *
+ from .modeling_idefics3 import *
+ from .processing_idefics3 import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/idefics3/configuration_idefics3.py b/third_party/transformers/src/transformers/models/idefics3/configuration_idefics3.py
new file mode 100644
index 0000000000000000000000000000000000000000..89a09dcf2f8485aa8fc169b22b123252897172fa
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/idefics3/configuration_idefics3.py
@@ -0,0 +1,110 @@
+# Copyright 2024 The HuggingFace Inc. team. All rights reserved.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Idefics3 model configuration"""
+
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...utils import auto_docstring, logging
+from ..auto import CONFIG_MAPPING, AutoConfig
+
+
+logger = logging.get_logger(__name__)
+
+
+@auto_docstring(checkpoint="HuggingFaceM4/Idefics3-8B-Llama3")
+@strict
+class Idefics3VisionConfig(PreTrainedConfig):
+ r"""
+ Example:
+
+ ```python
+ >>> from transformers.models.idefics3.modeling_idefics3 import Idefics3VisionTransformer
+ >>> from transformers.models.idefics3.configuration_idefics3 import Idefics3VisionConfig
+
+ >>> # Initializing a Idefics3VisionConfig with google/siglip-base-patch16-224 style configuration
+ >>> configuration = Idefics3VisionConfig()
+
+ >>> # Initializing a Idefics3VisionTransformer (with random weights) from the google/siglip-base-patch16-224 style configuration
+ >>> model = Idefics3VisionTransformer(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "idefics3_vision"
+ base_config_key = "vision_config"
+
+ hidden_size: int = 1152
+ intermediate_size: int = 3072
+ num_hidden_layers: int = 12
+ num_attention_heads: int = 16
+ num_channels: int = 3
+ image_size: int | list[int] | tuple[int, int] = 224
+ patch_size: int | list[int] | tuple[int, int] = 32
+ hidden_act: str = "gelu_pytorch_tanh"
+ layer_norm_eps: float = 1e-6
+ attention_dropout: float | int = 0.0
+ initializer_range: float = 0.02
+
+
+@auto_docstring(checkpoint="HuggingFaceM4/Idefics3-8B-Llama3")
+@strict
+class Idefics3Config(PreTrainedConfig):
+ r"""
+ scale_factor (`int`, *optional*, defaults to 2):
+ The scale factor for the image encoder.
+
+ Example:
+ ```python
+ >>> from transformers import Idefics3Model, Idefics3Config
+ >>> # Initializing configuration
+ >>> configuration = Idefics3Config()
+ >>> # Initializing a model from the configuration
+ >>> model = Idefics3Model(configuration)
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "idefics3"
+ sub_configs = {"text_config": AutoConfig, "vision_config": Idefics3VisionConfig}
+
+ use_cache: bool = True
+ image_token_id: int = 128257
+ tie_word_embeddings: bool = False
+ vision_config: dict | PreTrainedConfig | None = None
+ text_config: dict | PreTrainedConfig | None = None
+ scale_factor: int = 2
+ pad_token_id: int | None = 128_002
+
+ def __post_init__(self, **kwargs):
+ if self.vision_config is None:
+ self.vision_config = Idefics3VisionConfig()
+ logger.info("vision_config is None, using default vision config")
+ elif isinstance(self.vision_config, dict):
+ self.vision_config = Idefics3VisionConfig(**self.vision_config)
+
+ if isinstance(self.text_config, dict):
+ self.text_config["model_type"] = self.text_config.get("model_type", "llama")
+ self.text_config = CONFIG_MAPPING[self.text_config["model_type"]](**self.text_config)
+ elif self.text_config is None:
+ logger.info("text_config is None, using default Llama text config")
+ self.text_config = CONFIG_MAPPING["llama"](
+ rms_norm_eps=1e-5,
+ pad_token_id=self.pad_token_id,
+ )
+
+ super().__post_init__(**kwargs)
+
+
+__all__ = ["Idefics3Config", "Idefics3VisionConfig"]
diff --git a/third_party/transformers/src/transformers/models/idefics3/convert_idefics3_weights_to_hf.py b/third_party/transformers/src/transformers/models/idefics3/convert_idefics3_weights_to_hf.py
new file mode 100644
index 0000000000000000000000000000000000000000..382a1d238abc3719afe4dfbc84d4a80864ca145b
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/idefics3/convert_idefics3_weights_to_hf.py
@@ -0,0 +1,213 @@
+# Copyright 2024 The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import json
+
+import torch
+from huggingface_hub import hf_hub_download
+
+from transformers import (
+ AutoModelForCausalLM,
+ AutoTokenizer,
+ Idefics3Config,
+ Idefics3ForConditionalGeneration,
+ Idefics3ImageProcessor,
+ Idefics3Processor,
+ LlamaConfig,
+)
+
+
+EPILOG_TXT = """Example:
+ python transformers/src/transformers/models/idefics3/convert_idefics3_weights_to_hf.py --original_model_id HuggingFaceM4/Idefics3-8B-Llama3 --output_hub_path org/idefics3
+"""
+
+
+KEYS_TO_MODIFY_MAPPING = {
+ "lm_head.weight": "lm_head.linear.weight",
+ "model.layers": "model.text_model.layers",
+ "model.norm": "model.text_model.norm",
+ "model.modality_projection": "model.connector.modality_projection",
+}
+
+
+WEIGHTS_TO_MERGE_MAPPING = (
+ # (weights to merge in merging order), (new weight name)
+ (
+ ("model.embed_tokens.weight", "model.embed_tokens.additional_embedding.weight"),
+ "model.text_model.embed_tokens.weight",
+ ),
+ (("lm_head.linear.weight", "additional_fc.weight"), "lm_head.weight"),
+)
+
+WEIGHTS_TO_DROP = (
+ # The original model had a vision head, but this is never used
+ "model.vision_model.head",
+)
+
+
+def convert_state_dict_to_hf(state_dict):
+ new_state_dict = {}
+ old_state_dict_keys = set(state_dict.keys())
+
+ # Flattened list of weights to merge. We keep these in the original state dict to merge them later
+ original_weights_to_merge = [w for weights in WEIGHTS_TO_MERGE_MAPPING for w in weights[0]]
+
+ # for key, value in state_dict.items():
+ for old_key in old_state_dict_keys:
+ if old_key.endswith(".inv_freq") or any(w in old_key for w in WEIGHTS_TO_DROP):
+ state_dict.pop(old_key)
+ continue
+
+ key = old_key
+ for key_to_modify, new_key in KEYS_TO_MODIFY_MAPPING.items():
+ if key_to_modify in key:
+ key = key.replace(key_to_modify, new_key)
+
+ weight = state_dict.pop(old_key)
+ if key in original_weights_to_merge:
+ new_state_dict[key] = weight
+ # Bit of a hack - we need to keep the original weights to merge them later
+ state_dict[key] = weight
+ else:
+ new_state_dict[key] = weight
+
+ return new_state_dict
+
+
+def merge_weights(state_dict, new_state_dict):
+ old_weight_names = set(state_dict.keys())
+
+ # Merge the weights
+ for weights_to_merge, new_weight_name in WEIGHTS_TO_MERGE_MAPPING:
+ for weight_to_merge in weights_to_merge:
+ print(weight_to_merge)
+ assert weight_to_merge in state_dict, f"Weight {weight_to_merge} is missing in the state dict"
+
+ weight = state_dict.pop(weight_to_merge)
+ if new_weight_name not in new_state_dict:
+ new_state_dict[new_weight_name] = [weight]
+ else:
+ new_state_dict[new_weight_name].append(weight)
+
+ old_weight_names.remove(weight_to_merge)
+
+ new_state_dict[new_weight_name] = torch.cat(new_state_dict[new_weight_name], dim=0)
+
+ # Remove the weights that were merged
+ for weights_to_merge, new_weight_name in WEIGHTS_TO_MERGE_MAPPING:
+ for weight in weights_to_merge:
+ if weight in new_state_dict and weight != new_weight_name:
+ new_state_dict.pop(weight)
+
+ return new_state_dict
+
+
+def get_config(checkpoint):
+ # We load the config then recreate to use the text_config
+
+ # download the config file
+ filepath = hf_hub_download(repo_id=checkpoint, filename="config.json")
+ with open(filepath, "r") as f:
+ config_json = json.load(f)
+
+ # Setup the vision config
+ vision_config = config_json.pop("vision_config")
+ vision_config.pop("vision_model_name", None)
+ if "embed_dim" in vision_config:
+ vision_config["hidden_size"] = vision_config.pop("embed_dim")
+
+ config_json["vocab_size"] = config_json.pop("vocab_size") + config_json.pop("additional_vocab_size")
+
+ image_token_id = config_json.pop("image_token_id", config_json["vocab_size"] - 2)
+ use_cache = config_json.pop("use_cache", True)
+ tie_word_embeddings = config_json.pop("tie_word_embeddings", True)
+ scale_factor = config_json.pop("scale_factor", 2)
+ vocab_size = config_json.pop("vocab_size", 100000)
+
+ # Remove "freeze" params from the config
+ config_json = {k: v for k, v in config_json.items() if not k.startswith("freeze_")}
+ text_config = LlamaConfig(**config_json)
+
+ config = Idefics3Config(
+ text_config=text_config,
+ vision_config=vision_config,
+ use_cache=use_cache,
+ image_token_id=image_token_id,
+ tie_word_embeddings=tie_word_embeddings,
+ scale_factor=scale_factor,
+ vocab_size=vocab_size,
+ )
+ return config
+
+
+def convert_idefics3_hub_to_hf(original_model_id, output_hub_path, push_to_hub):
+ # The original model maps to AutoModelForCausalLM, converted we map to Idefics3ForConditionalGeneration
+ original_model = AutoModelForCausalLM.from_pretrained(
+ original_model_id, trust_remote_code=True, dtype=torch.bfloat16
+ )
+ # The original model doesn't use the Idefics3 processing objects
+ image_processor = Idefics3ImageProcessor()
+ tokenizer = AutoTokenizer.from_pretrained(original_model_id)
+ processor = Idefics3Processor(
+ image_processor=image_processor,
+ tokenizer=tokenizer,
+ )
+ state_dict = original_model.state_dict()
+ new_state_dict = convert_state_dict_to_hf(state_dict)
+
+ # Merge weights
+ new_state_dict = merge_weights(state_dict, new_state_dict)
+ del state_dict
+
+ config = get_config(original_model_id)
+ print(config)
+
+ with torch.device("meta"):
+ model = Idefics3ForConditionalGeneration(config)
+
+ model.load_state_dict(new_state_dict, strict=True, assign=True)
+
+ model.save_pretrained(output_hub_path)
+ processor.save_pretrained(output_hub_path)
+
+ if push_to_hub:
+ model.push_to_hub(output_hub_path, private=True)
+ processor.push_to_hub(output_hub_path, private=True)
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ epilog=EPILOG_TXT,
+ formatter_class=argparse.RawDescriptionHelpFormatter,
+ )
+ parser.add_argument(
+ "--original_model_id",
+ help="Hub location of the text model",
+ )
+ parser.add_argument(
+ "--output_hub_path",
+ help="Location on the hub of the converted model",
+ )
+ parser.add_argument(
+ "--push_to_hub",
+ action="store_true",
+ help="If set, the model will be pushed to the hub after conversion.",
+ )
+ args = parser.parse_args()
+ convert_idefics3_hub_to_hf(args.original_model_id, args.output_hub_path, args.push_to_hub)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/third_party/transformers/src/transformers/models/idefics3/image_processing_idefics3.py b/third_party/transformers/src/transformers/models/idefics3/image_processing_idefics3.py
new file mode 100644
index 0000000000000000000000000000000000000000..96f7699b8220515e3f2a247f5d62cf92c07620d8
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/idefics3/image_processing_idefics3.py
@@ -0,0 +1,551 @@
+# Copyright 2025 The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Image processor class for Idefics3."""
+
+import math
+
+import numpy as np
+import torch
+from torchvision.transforms.v2 import functional as tvF
+
+from ...image_processing_backends import TorchvisionBackend
+from ...image_processing_utils import BatchFeature
+from ...image_transforms import group_images_by_shape, reorder_images
+from ...image_utils import (
+ IMAGENET_STANDARD_MEAN,
+ IMAGENET_STANDARD_STD,
+ ImageInput,
+ PILImageResampling,
+ SizeDict,
+ make_nested_list_of_images,
+)
+from ...processing_utils import ImagesKwargs, Unpack
+from ...utils import TensorType, auto_docstring, logging
+
+
+logger = logging.get_logger(__name__)
+
+MAX_IMAGE_SIZE = 4096 # 4k resolution as absolute maximum
+
+
+class Idefics3ImageProcessorKwargs(ImagesKwargs, total=False):
+ """
+ do_image_splitting (`bool`, *optional*, defaults to `True`):
+ Whether to split the image into sub-images concatenated with the original image. They are split into patches
+ such that each patch has a size of `max_image_size["height"]` x `max_image_size["width"]`.
+ max_image_size (`Dict`, *optional*, defaults to `{"longest_edge": 364}`):
+ Maximum resolution of the patches of images accepted by the model. This is a dictionary containing the key "longest_edge".
+ return_row_col_info (`bool`, *optional*, defaults to `False`):
+ Whether to return the row and column information of the images.
+ """
+
+ do_image_splitting: bool
+ max_image_size: dict[str, int]
+ return_row_col_info: bool
+
+
+def _resize_output_size_rescale_to_max_len(
+ height: int, width: int, min_len: int | None = 1, max_len: int | None = None
+) -> tuple[int, int]:
+ """
+ Get the output size of the image after resizing given a dictionary specifying the max and min sizes.
+ Args:
+ height (`int`):
+ Height of the input image.
+ width (`int`):
+ Width of the input image.
+ min_len (`int`, *optional*, defaults to 1):
+ Minimum size of the output image.
+ max_len (`int`, *optional*, defaults to the maximum size of the image):
+ Maximum size of the output image.
+ Returns:
+ The output size of the image after resizing.
+ """
+ max_len = max(height, width) if max_len is None else max_len
+ aspect_ratio = width / height
+
+ if width >= height:
+ width = max_len
+ height = int(width / aspect_ratio)
+ if height % 2 != 0:
+ height += 1
+ elif height > width:
+ height = max_len
+ width = int(height * aspect_ratio)
+ if width % 2 != 0:
+ width += 1
+
+ # Avoid resizing to a size smaller than min_len
+ height = max(height, min_len)
+ width = max(width, min_len)
+ return height, width
+
+
+def _resize_output_size_scale_below_upper_bound(
+ height: int, width: int, max_len: dict[str, int] | None = None
+) -> tuple[int, int]:
+ """
+ Get the output size of the image after resizing given a dictionary specifying the max and min sizes.
+ Args:
+ height (`int`):
+ Height of the input image.
+ width (`int`):
+ Width of the input image.
+ max_len (`Dict[str, int]`, *optional*, defaults to the maximum size of the image):
+ Defines the maximum dimensions of the image.
+ Returns:
+ The output size of the image after resizing.
+ """
+ max_len = max(height, width) if max_len is None else max_len
+
+ aspect_ratio = width / height
+ if width >= height and width > max_len:
+ width = max_len
+ height = int(width / aspect_ratio)
+ elif height > width and height > max_len:
+ height = max_len
+ width = int(height * aspect_ratio)
+
+ # Avoid resizing to a size smaller than 1
+ height = max(height, 1)
+ width = max(width, 1)
+ return height, width
+
+
+def get_resize_output_image_size(
+ image: "torch.Tensor",
+ resolution_max_side: int,
+) -> tuple[int, int]:
+ """
+ Get the output size of the image after resizing given a dictionary specifying the max and min sizes.
+ Args:
+ image (`torch.Tensor`):
+ Image to resize.
+ resolution_max_side (`int`):
+ The longest edge of the image will be resized to this value. The shortest edge will be resized to keep the
+ input aspect ratio.
+ Returns:
+ The output size of the image after resizing.
+ """
+ height, width = image.shape[-2:]
+
+ # Find the output size, when rescaling the longest edge to max_len and preserving the aspect ratio
+ height, width = _resize_output_size_rescale_to_max_len(height, width, max_len=resolution_max_side)
+ # Find the output size when scaling the image to be below the MAX_IMAGE_SIZE
+ height, width = _resize_output_size_scale_below_upper_bound(height, width, max_len=MAX_IMAGE_SIZE)
+ return height, width
+
+
+def get_max_height_width(images_list: list[list["torch.Tensor|np.ndarray"]]) -> tuple[int, int]:
+ """
+ Get the maximum height and width across all images in a batch.
+ """
+ image_sizes = []
+ for images in images_list:
+ for image in images:
+ image_sizes.append(image.shape[-2:])
+
+ max_height = max(size[0] for size in image_sizes)
+ max_width = max(size[1] for size in image_sizes)
+ return (max_height, max_width)
+
+
+def get_num_channels(images_list: list[list["torch.Tensor|np.ndarray"]]) -> int:
+ """
+ Get the number of channels across all images in a batch. Handle empty sublists like in [[], [image]].
+ """
+ for images in images_list:
+ if images:
+ return images[0].shape[0]
+
+ raise ValueError("No images found in the batch.")
+
+
+def get_device_from_images(images_list: list[list["torch.Tensor"]]) -> "torch.device":
+ """
+ Get the device from the first non-empty element in a nested list of images.
+ Handle empty sublists like in [[], [image]].
+ """
+ for images in images_list:
+ if images:
+ return images[0].device
+
+
+def make_pixel_mask(image: "torch.Tensor", output_size: tuple[int, int]) -> "torch.Tensor":
+ """
+ Make a pixel mask for the image, where 1 indicates a valid pixel and 0 indicates padding.
+
+ Args:
+ image (`torch.Tensor`):
+ Image to make the pixel mask for.
+ output_size (`Tuple[int, int]`):
+ Output size of the mask.
+ """
+ input_height, input_width = image.shape[-2:]
+ mask = torch.zeros(output_size, dtype=torch.int64, device=image.device)
+ mask[:input_height, :input_width] = 1
+ return mask
+
+
+@auto_docstring
+class Idefics3ImageProcessor(TorchvisionBackend):
+ resample = PILImageResampling.LANCZOS
+ image_mean = IMAGENET_STANDARD_MEAN
+ image_std = IMAGENET_STANDARD_STD
+ size = {"longest_edge": 4 * 364}
+ max_image_size = {"longest_edge": 364}
+ do_resize = True
+ do_rescale = True
+ do_normalize = True
+ do_convert_rgb = True
+ do_image_splitting = True
+ do_pad = True
+ return_row_col_info = False
+ valid_kwargs = Idefics3ImageProcessorKwargs
+ model_input_names = ["pixel_values", "pixel_attention_mask"]
+
+ def __init__(self, **kwargs: Unpack[Idefics3ImageProcessorKwargs]):
+ super().__init__(**kwargs)
+
+ @auto_docstring
+ def preprocess(self, images: ImageInput, **kwargs: Unpack[Idefics3ImageProcessorKwargs]) -> BatchFeature:
+ return super().preprocess(images, **kwargs)
+
+ def _prepare_images_structure(self, images: ImageInput, expected_ndims: int = 3) -> ImageInput:
+ """
+ Prepare a nested images structure for processing.
+ """
+ # Checks for `str` in case of URL/local path and optionally loads images
+ images = self.fetch_images(images)
+ return make_nested_list_of_images(images, expected_ndims=expected_ndims)
+
+ def resize(
+ self,
+ image: "torch.Tensor",
+ size: SizeDict,
+ resample: "PILImageResampling | tvF.InterpolationMode | int | None" = None,
+ **kwargs,
+ ) -> "torch.Tensor":
+ """
+ Resize an image. The longest edge of the image is resized to size.longest_edge, with the shortest edge
+ resized to keep the input aspect ratio. Can also be used with size.height and size.width.
+ Args:
+ image (`torch.Tensor`):
+ Image to resize.
+ size (`SizeDict`):
+ Size of the output image.
+ resample (`PILImageResampling | tvF.InterpolationMode | int | None`, *optional*):
+ Resampling filter to use when resizing the image.
+ """
+ if size.longest_edge:
+ new_size = get_resize_output_image_size(image, resolution_max_side=size.longest_edge)
+ elif size.height and size.width:
+ new_size = (size.height, size.width)
+ else:
+ raise ValueError("size must be a dictionary with key 'longest_edge' or 'height' and 'width'.")
+
+ return super().resize(image, SizeDict(height=new_size[0], width=new_size[1]), resample=resample, **kwargs)
+
+ def split_images(
+ self,
+ images: torch.Tensor,
+ max_image_size: dict[str, int],
+ resample: "PILImageResampling | tvF.InterpolationMode | int | None" = None,
+ ):
+ """
+ Split an image into squares of side max_image_size and the original image resized to max_image_size.
+ That means that a single image becomes a sequence of images.
+ This is a "trick" to spend more compute on each image with no changes in the vision encoder.
+ 1) If one side of the original image is larger than `max_image_size`, resize it to `max_image_size` while preserving the aspect ratio.
+ 2) Divide the resulting image into `ceil(height / max_image_size)` x `ceil(width / max_image_size)`
+ sub-images of the same size each (image_size, image_size). Typically, 364x364.
+ 3) Returns the list of the crops and the original image, in addition to the number of splits for the height and the width.
+ Args:
+ images (`torch.Tensor`):
+ Images to split.
+ max_image_size (`Dict[str, int]`):
+ Maximum size of the output image. If the image is larger than this size, it will be split into
+ patches of this size, and the original image will be concatenated with the patches, resized to max_size.
+ resample (`PILImageResampling | tvF.InterpolationMode | int | None`, *optional*):
+ Resampling filter to use when resizing the image.
+ """
+ batch_size, num_channels, height, width = images.size()
+ height_dim, width_dim = 2, 3
+
+ max_height = max_width = max_image_size["longest_edge"]
+
+ frames = []
+ if height > max_height or width > max_width:
+ # Calculate the number of splits
+ num_splits_h = math.ceil(height / max_height)
+ num_splits_w = math.ceil(width / max_width)
+
+ # Split the images by height, then by width
+ frames = (
+ images.unfold(height_dim, size=max_height, step=max_height)
+ .unfold(width_dim, size=max_width, step=max_width)
+ .contiguous()
+ .view(batch_size, num_channels, -1, max_height, max_width)
+ .permute(0, 2, 1, 3, 4)
+ ) # batch_size x n_frames x num_channels x height x width
+
+ # For the global image at the end, we resize it to match the max_image_size, for cpu memory efficiency
+ global_image_height, global_image_width = max_height, max_width
+ images = self.resize(
+ images, SizeDict(height=global_image_height, width=global_image_width), resample=resample
+ )
+
+ frames = torch.cat((frames, images.unsqueeze(1)), dim=1)
+ else:
+ num_splits_h, num_splits_w = 0, 0
+ frames = images.unsqueeze(1)
+
+ num_splits_h = [num_splits_h] * batch_size
+ num_splits_w = [num_splits_w] * batch_size
+
+ return frames, num_splits_h, num_splits_w
+
+ def resize_for_vision_encoder(
+ self,
+ image: torch.Tensor,
+ vision_encoder_max_size: int,
+ resample: "PILImageResampling | tvF.InterpolationMode | int | None" = None,
+ ):
+ """
+ Resize images to be multiples of `vision_encoder_max_size` while preserving the aspect ratio.
+ Args:
+ image (`torch.Tensor`):
+ Images to resize.
+ vision_encoder_max_size (`int`):
+ Maximum size of the output image. If the image is larger than this size, it will be split into
+ patches of this size, and the original image will be concatenated with the patches, resized to max_size.
+ resample (`PILImageResampling | tvF.InterpolationMode | int | None`, *optional*):
+ Resampling filter to use when resizing the image.
+ """
+ height, width = image.size()[-2:]
+
+ aspect_ratio = width / height
+ if width >= height:
+ width = math.ceil(width / vision_encoder_max_size) * vision_encoder_max_size
+ height = int(width / aspect_ratio)
+ height = math.ceil(height / vision_encoder_max_size) * vision_encoder_max_size
+ elif height > width:
+ height = math.ceil(height / vision_encoder_max_size) * vision_encoder_max_size
+ width = int(height * aspect_ratio)
+ width = math.ceil(width / vision_encoder_max_size) * vision_encoder_max_size
+ new_size = SizeDict(height=height, width=width)
+ return self.resize(image, size=new_size, resample=resample)
+
+ def pad(
+ self,
+ image: torch.Tensor,
+ padded_size: tuple[int, int],
+ fill: int = 0,
+ return_pixel_mask: bool = True,
+ ):
+ original_size = image.shape[-2:]
+ padding_bottom = padded_size[0] - original_size[0]
+ padding_right = padded_size[1] - original_size[1]
+
+ if padding_bottom < 0 or padding_right < 0:
+ raise ValueError(
+ f"Padding dimensions are negative. Please make sure that the padded size is larger than the "
+ f"original size. Got padded size: {padded_size}, original size: {original_size}."
+ )
+
+ # Only pad if necessary
+ if original_size != padded_size:
+ padding = (0, 0, padding_right, padding_bottom)
+ image = tvF.pad(image, padding, fill=fill, padding_mode="constant")
+
+ # Make a pixel mask for the image, where 1 indicates a valid pixel and 0 indicates padding.
+ pixel_mask = None
+ if return_pixel_mask:
+ pixel_mask = torch.zeros_like(image[..., 0, :, :], dtype=torch.int64)
+ pixel_mask[: original_size[0], : original_size[1]] = 1
+
+ return image, pixel_mask
+
+ def _preprocess(
+ self,
+ images: list[list["torch.Tensor"]],
+ do_resize: bool,
+ size: SizeDict,
+ resample: "PILImageResampling | tvF.InterpolationMode | int | None",
+ do_rescale: bool,
+ rescale_factor: float,
+ do_normalize: bool,
+ image_mean: float | list[float] | None,
+ image_std: float | list[float] | None,
+ do_pad: bool | None,
+ do_image_splitting: bool | None,
+ max_image_size: dict[str, int] | None,
+ return_row_col_info: bool | None,
+ disable_grouping: bool | None,
+ return_tensors: str | TensorType | None,
+ **kwargs,
+ ) -> BatchFeature:
+ """
+ Process a batch of images for the model.
+ """
+
+ grouped_images, grouped_images_index = group_images_by_shape(
+ images, is_nested=True, disable_grouping=disable_grouping
+ )
+ resized_images_grouped = {}
+ for shape, stacked_images in grouped_images.items():
+ if do_resize:
+ stacked_images = self.resize(stacked_images, size, resample=resample)
+ resized_images_grouped[shape] = stacked_images
+ resized_images = reorder_images(resized_images_grouped, grouped_images_index, is_nested=True)
+
+ grouped_images, grouped_images_index = group_images_by_shape(
+ resized_images, is_nested=True, disable_grouping=disable_grouping
+ )
+ split_images_grouped = {}
+ if do_image_splitting:
+ rows_grouped = {}
+ cols_grouped = {}
+ for shape, stacked_images in grouped_images.items():
+ stacked_images = self.resize_for_vision_encoder(
+ stacked_images, max_image_size["longest_edge"], resample=resample
+ )
+ stacked_images, rows, cols = self.split_images(
+ stacked_images, max_image_size=max_image_size, resample=resample
+ )
+ split_images_grouped[shape] = stacked_images
+ rows_grouped[shape] = rows
+ cols_grouped[shape] = cols
+ processed_images = reorder_images(split_images_grouped, grouped_images_index, is_nested=True)
+ rows = reorder_images(rows_grouped, grouped_images_index, is_nested=True)
+ cols = reorder_images(cols_grouped, grouped_images_index, is_nested=True)
+ # flattenened the doubly nested list to a nested list
+ for i, group_images in enumerate(processed_images):
+ processed_images[i] = [image for sublist in group_images for image in sublist]
+ else:
+ for shape, stacked_images in grouped_images.items():
+ # We square the images to max_image_size
+ stacked_images = self.resize(
+ image=stacked_images,
+ size=SizeDict(height=max_image_size["longest_edge"], width=max_image_size["longest_edge"]),
+ resample=resample,
+ )
+ split_images_grouped[shape] = stacked_images
+ processed_images = reorder_images(split_images_grouped, grouped_images_index, is_nested=True)
+ rows = [[0] * len(images) for images in processed_images]
+ cols = [[0] * len(images) for images in processed_images]
+ # Group images by size for further processing
+ # Needed in case do_resize is False, or resize returns images with different sizes
+ grouped_images, grouped_images_index = group_images_by_shape(
+ processed_images, is_nested=True, disable_grouping=disable_grouping
+ )
+ processed_images_grouped = {}
+ for shape, stacked_images in grouped_images.items():
+ # Fused rescale and normalize
+ stacked_images = self.rescale_and_normalize(
+ stacked_images, do_rescale, rescale_factor, do_normalize, image_mean, image_std
+ )
+ processed_images_grouped[shape] = stacked_images
+ processed_images = reorder_images(processed_images_grouped, grouped_images_index, is_nested=True)
+ if do_pad:
+ # Get max images per batch
+ max_num_images = max(len(images_) for images_ in processed_images)
+ max_height, max_width = get_max_height_width(processed_images)
+ num_channels = get_num_channels(processed_images)
+ device = get_device_from_images(processed_images)
+
+ processed_images_padded = torch.zeros(
+ len(processed_images),
+ max_num_images,
+ *(num_channels, max_height, max_width),
+ device=device,
+ )
+ pixel_attention_masks = torch.zeros(
+ len(processed_images),
+ max_num_images,
+ *(max_height, max_width),
+ device=device,
+ )
+ for i, images in enumerate(processed_images):
+ for j, image in enumerate(images):
+ processed_images_padded[i, j], pixel_attention_masks[i, j] = self.pad(
+ image, (max_height, max_width)
+ )
+ processed_images = processed_images_padded
+
+ if do_pad:
+ data = {"pixel_values": processed_images, "pixel_attention_mask": pixel_attention_masks}
+ elif return_tensors == "pt":
+ data = {"pixel_values": torch.stack([torch.stack(images) for images in processed_images])}
+ else:
+ data = {"pixel_values": processed_images}
+ # This is needed for generating correct text inputs in the processor - we don't pad to the max number of images
+ encoding = BatchFeature(data=data, tensor_type=return_tensors)
+
+ if return_row_col_info:
+ encoding["rows"] = rows
+ encoding["cols"] = cols
+
+ return encoding
+
+ def to_dict(self):
+ encoder_dict = super().to_dict()
+ encoder_dict.pop("_valid_processor_keys", None)
+ encoder_dict.pop("return_row_col_info", None)
+ return encoder_dict
+
+ def get_number_of_image_patches(self, height: int, width: int, images_kwargs: dict):
+ """
+ A utility that returns number of image patches for a given image size.
+
+ Args:
+ height (`int`):
+ Height of the input image.
+ width (`int`):
+ Width of the input image.
+ images_kwargs (`dict`)
+ Any kwargs to override defaults of the image processor.
+ Returns:
+ `int`: Number of patches per image.
+ """
+ do_image_splitting = images_kwargs.get("do_image_splitting", self.do_image_splitting)
+ max_image_size = images_kwargs.get("max_image_size", self.max_image_size)
+ size = images_kwargs.get("size", self.size)
+
+ num_patches = num_rows = num_cols = 0
+ if do_image_splitting:
+ height, width = _resize_output_size_rescale_to_max_len(height, width, max_len=size["longest_edge"])
+ height, width = _resize_output_size_scale_below_upper_bound(height, width, max_len=MAX_IMAGE_SIZE)
+ aspect_ratio = width / height
+
+ if width >= height:
+ resized_width = math.ceil(width / max_image_size["longest_edge"]) * max_image_size["longest_edge"]
+ resized_height = int(width / aspect_ratio)
+ resized_height = math.ceil(height / max_image_size["longest_edge"]) * max_image_size["longest_edge"]
+ elif height > width:
+ resized_height = math.ceil(height / max_image_size["longest_edge"]) * max_image_size["longest_edge"]
+ resized_width = int(height * aspect_ratio)
+ resized_width = math.ceil(width / max_image_size["longest_edge"]) * max_image_size["longest_edge"]
+
+ max_height = max_width = max_image_size["longest_edge"]
+ if resized_height > max_height or resized_width > max_width:
+ # Calculate the number of splits
+ num_rows = math.ceil(resized_height / max_height)
+ num_cols = math.ceil(resized_width / max_width)
+ num_patches = num_rows * num_cols + 1
+
+ return num_patches, num_rows, num_cols
+
+
+__all__ = ["Idefics3ImageProcessor"]
diff --git a/third_party/transformers/src/transformers/models/idefics3/image_processing_pil_idefics3.py b/third_party/transformers/src/transformers/models/idefics3/image_processing_pil_idefics3.py
new file mode 100644
index 0000000000000000000000000000000000000000..7f2ffaf50892bb163157fa7b3926e549c8baf676
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/idefics3/image_processing_pil_idefics3.py
@@ -0,0 +1,480 @@
+# Copyright 2025 The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""PIL Image processor class for Idefics3."""
+
+import math
+from typing import TYPE_CHECKING
+
+import numpy as np
+
+from ...image_processing_backends import PilBackend
+from ...image_processing_utils import BatchFeature
+from ...image_transforms import PaddingMode, pad
+from ...image_utils import (
+ IMAGENET_STANDARD_MEAN,
+ IMAGENET_STANDARD_STD,
+ ImageInput,
+ PILImageResampling,
+ SizeDict,
+ make_nested_list_of_images,
+)
+from ...processing_utils import ImagesKwargs, Unpack
+from ...utils import TensorType, auto_docstring
+
+
+if TYPE_CHECKING:
+ pass
+
+
+def _make_pixel_mask(image: np.ndarray, output_size: tuple[int, int]) -> np.ndarray:
+ """Make pixel mask: 1=valid, 0=padding. Images are CHW."""
+ h, w = image.shape[-2:]
+ mask = np.zeros(output_size, dtype=np.int64)
+ mask[:h, :w] = 1
+ return mask
+
+
+# Adapted from transformers.models.idefics3.image_processing_idefics3.MAX_IMAGE_SIZE
+MAX_IMAGE_SIZE = 4096 # 4k resolution as absolute maximum
+
+
+# Adapted from transformers.models.idefics3.image_processing_idefics3.Idefics3ImageProcessorKwargs
+class Idefics3ImageProcessorKwargs(ImagesKwargs, total=False):
+ """
+ do_image_splitting (`bool`, *optional*, defaults to `True`):
+ Whether to split the image into sub-images concatenated with the original image. They are split into patches
+ such that each patch has a size of `max_image_size["height"]` x `max_image_size["width"]`.
+ max_image_size (`Dict`, *optional*, defaults to `{"longest_edge": 364}`):
+ Maximum resolution of the patches of images accepted by the model. This is a dictionary containing the key "longest_edge".
+ return_row_col_info (`bool`, *optional*, defaults to `False`):
+ Whether to return the row and column information of the images.
+ """
+
+ do_image_splitting: bool
+ max_image_size: dict[str, int]
+ return_row_col_info: bool
+
+
+# Adapted from transformers.models.idefics3.image_processing_idefics3._resize_output_size_rescale_to_max_len
+def _resize_output_size_rescale_to_max_len(
+ height: int, width: int, min_len: int | None = 1, max_len: int | None = None
+) -> tuple[int, int]:
+ """
+ Get the output size of the image after resizing given a dictionary specifying the max and min sizes.
+ Args:
+ height (`int`):
+ Height of the input image.
+ width (`int`):
+ Width of the input image.
+ min_len (`int`, *optional*, defaults to 1):
+ Minimum size of the output image.
+ max_len (`int`, *optional*, defaults to the maximum size of the image):
+ Maximum size of the output image.
+ Returns:
+ The output size of the image after resizing.
+ """
+ max_len = max(height, width) if max_len is None else max_len
+ aspect_ratio = width / height
+
+ if width >= height:
+ width = max_len
+ height = int(width / aspect_ratio)
+ if height % 2 != 0:
+ height += 1
+ elif height > width:
+ height = max_len
+ width = int(height * aspect_ratio)
+ if width % 2 != 0:
+ width += 1
+
+ # Avoid resizing to a size smaller than min_len
+ height = max(height, min_len)
+ width = max(width, min_len)
+ return height, width
+
+
+# Adapted from transformers.models.idefics3.image_processing_idefics3._resize_output_size_scale_below_upper_bound
+def _resize_output_size_scale_below_upper_bound(
+ height: int, width: int, max_len: dict[str, int] | None = None
+) -> tuple[int, int]:
+ """
+ Get the output size of the image after resizing given a dictionary specifying the max and min sizes.
+ Args:
+ height (`int`):
+ Height of the input image.
+ width (`int`):
+ Width of the input image.
+ max_len (`Dict[str, int]`, *optional*, defaults to the maximum size of the image):
+ Defines the maximum dimensions of the image.
+ Returns:
+ The output size of the image after resizing.
+ """
+ max_len = max(height, width) if max_len is None else max_len
+
+ aspect_ratio = width / height
+ if width >= height and width > max_len:
+ width = max_len
+ height = int(width / aspect_ratio)
+ elif height > width and height > max_len:
+ height = max_len
+ width = int(height * aspect_ratio)
+
+ # Avoid resizing to a size smaller than 1
+ height = max(height, 1)
+ width = max(width, 1)
+ return height, width
+
+
+def get_max_height_width(images_list: list[list[np.ndarray]]) -> tuple[int, int]:
+ """
+ Get the maximum height and width across all images in a batch.
+ """
+ image_sizes = []
+ for images in images_list:
+ for image in images:
+ image_sizes.append(image.shape[-2:])
+
+ max_height = max(size[0] for size in image_sizes)
+ max_width = max(size[1] for size in image_sizes)
+ return (max_height, max_width)
+
+
+def get_num_channels(images_list: list[list[np.ndarray]]) -> int:
+ """
+ Get the number of channels across all images in a batch. Handle empty sublists like in [[], [image]].
+ """
+ for images in images_list:
+ if images:
+ return images[0].shape[0]
+
+ raise ValueError("No images found in the batch.")
+
+
+def get_resize_output_image_size(
+ image: np.ndarray,
+ resolution_max_side: int,
+) -> tuple[int, int]:
+ """
+ Get the output size of the image after resizing given a dictionary specifying the max and min sizes.
+ Args:
+ image (`np.ndarray`):
+ Image to resize.
+ resolution_max_side (`int`):
+ The longest edge of the image will be resized to this value. The shortest edge will be resized to keep the
+ input aspect ratio.
+ Returns:
+ The output size of the image after resizing.
+ """
+ height, width = image.shape[-2:]
+
+ # Find the output size, when rescaling the longest edge to max_len and preserving the aspect ratio
+ height, width = _resize_output_size_rescale_to_max_len(height, width, max_len=resolution_max_side)
+ # Find the output size when scaling the image to be below the MAX_IMAGE_SIZE
+ height, width = _resize_output_size_scale_below_upper_bound(height, width, max_len=MAX_IMAGE_SIZE)
+ return height, width
+
+
+@auto_docstring
+class Idefics3ImageProcessorPil(PilBackend):
+ resample = PILImageResampling.LANCZOS
+ image_mean = IMAGENET_STANDARD_MEAN
+ image_std = IMAGENET_STANDARD_STD
+ size = {"longest_edge": 4 * 364}
+ max_image_size = {"longest_edge": 364}
+ do_resize = True
+ do_rescale = True
+ do_normalize = True
+ do_convert_rgb = True
+ do_image_splitting = True
+ do_pad = True
+ return_row_col_info = False
+ valid_kwargs = Idefics3ImageProcessorKwargs
+ model_input_names = ["pixel_values", "pixel_attention_mask"]
+
+ def __init__(self, **kwargs: Unpack[Idefics3ImageProcessorKwargs]):
+ super().__init__(**kwargs)
+
+ @auto_docstring
+ def preprocess(self, images: ImageInput, **kwargs: Unpack[Idefics3ImageProcessorKwargs]) -> BatchFeature:
+ return super().preprocess(images, **kwargs)
+
+ def _prepare_images_structure(self, images: ImageInput, expected_ndims: int = 3) -> ImageInput:
+ images = self.fetch_images(images)
+ return make_nested_list_of_images(images, expected_ndims=expected_ndims)
+
+ def resize(
+ self,
+ image: np.ndarray,
+ size: SizeDict,
+ resample: PILImageResampling = PILImageResampling.LANCZOS,
+ **kwargs,
+ ) -> np.ndarray:
+ if size.longest_edge:
+ new_size = get_resize_output_image_size(image, resolution_max_side=size.longest_edge)
+ elif size.height and size.width:
+ new_size = (size.height, size.width)
+ else:
+ raise ValueError("size must be a dictionary with key 'longest_edge' or 'height' and 'width'.")
+ return super().resize(image, SizeDict(height=new_size[0], width=new_size[1]), resample=resample, **kwargs)
+
+ def split_images(
+ self,
+ image: np.ndarray,
+ max_image_size: dict[str, int],
+ resample: "PILImageResampling | None" = None,
+ ):
+ """Split an image into patches (mirrors TorchvisionBackend.split_images). Images are always CHW."""
+ num_channels, height, width = image.shape
+ max_height = max_width = max_image_size["longest_edge"]
+
+ if height > max_height or width > max_width:
+ num_splits_h = (height - max_height) // max_height + 1
+ num_splits_w = (width - max_width) // max_width + 1
+
+ frames = []
+ for r in range(num_splits_h):
+ for c in range(num_splits_w):
+ start_y = r * max_height
+ start_x = c * max_width
+ end_y = start_y + max_height
+ end_x = start_x + max_width
+ crop = image[:, start_y:end_y, start_x:end_x]
+ frames.append(crop)
+
+ global_image_height, global_image_width = max_height, max_width
+ image = self.resize(
+ image, SizeDict(height=global_image_height, width=global_image_width), resample=resample
+ )
+ frames.append(image)
+ else:
+ num_splits_h, num_splits_w = 0, 0
+ frames = [image]
+
+ return frames, num_splits_h, num_splits_w
+
+ def resize_for_vision_encoder(
+ self,
+ image: np.ndarray,
+ vision_encoder_max_size: int,
+ resample: "PILImageResampling | None" = None,
+ ):
+ """Resize images to be multiples of vision_encoder_max_size. Images are always CHW."""
+ height, width = image.shape[-2:]
+ aspect_ratio = width / height
+ if width >= height:
+ width = math.ceil(width / vision_encoder_max_size) * vision_encoder_max_size
+ height = int(width / aspect_ratio)
+ height = math.ceil(height / vision_encoder_max_size) * vision_encoder_max_size
+ elif height > width:
+ height = math.ceil(height / vision_encoder_max_size) * vision_encoder_max_size
+ width = int(height * aspect_ratio)
+ width = math.ceil(width / vision_encoder_max_size) * vision_encoder_max_size
+ new_size = SizeDict(height=height, width=width)
+ return self.resize(image, size=new_size, resample=resample)
+
+ def pad(
+ self,
+ image: np.ndarray,
+ padded_size: tuple[int, int],
+ fill: int = 0,
+ return_pixel_mask: bool = True,
+ ):
+ """Pad image to padded_size. Mirrors TorchvisionBackend.pad. Images are always CHW."""
+ original_size = image.shape[-2:]
+ padding_bottom = padded_size[0] - original_size[0]
+ padding_right = padded_size[1] - original_size[1]
+
+ if padding_bottom < 0 or padding_right < 0:
+ raise ValueError(
+ f"Padding dimensions are negative. Please make sure that the padded size is larger than the "
+ f"original size. Got padded size: {padded_size}, original size: {original_size}."
+ )
+
+ pixel_mask = _make_pixel_mask(image, output_size=padded_size) if return_pixel_mask else None
+
+ if original_size != padded_size:
+ padding = ((0, padding_bottom), (0, padding_right))
+ image = pad(
+ image,
+ padding,
+ mode=PaddingMode.CONSTANT,
+ constant_values=fill,
+ data_format="channels_first",
+ input_data_format="channels_first",
+ )
+
+ return image, pixel_mask
+
+ def _preprocess(
+ self,
+ images: list[list[np.ndarray]],
+ do_resize: bool,
+ size: SizeDict,
+ resample: "PILImageResampling | None",
+ do_rescale: bool,
+ rescale_factor: float,
+ do_normalize: bool,
+ image_mean: float | list[float] | None,
+ image_std: float | list[float] | None,
+ do_pad: bool | None,
+ do_image_splitting: bool | None,
+ max_image_size: dict[str, int] | None,
+ return_row_col_info: bool | None,
+ return_tensors: str | TensorType | None,
+ **kwargs,
+ ) -> BatchFeature:
+ """Process a batch of images. Mirrors TorchvisionBackend._preprocess with per-image loops instead of batching."""
+ # Resize
+ if do_resize:
+ images = [
+ [self.resize(image=img, size=size, resample=resample) for img in batch_images]
+ for batch_images in images
+ ]
+
+ # Image splitting
+ if do_image_splitting:
+ images = [
+ [
+ self.resize_for_vision_encoder(image, max_image_size["longest_edge"], resample=resample)
+ for image in batch_images
+ ]
+ for batch_images in images
+ ]
+ images_split_arrays = []
+ images_rows = []
+ images_cols = []
+ for batch_images in images:
+ split_image_arrays = []
+ image_rows = []
+ image_cols = []
+ for image in batch_images:
+ split_image_array, rows, cols = self.split_images(
+ image, max_image_size=max_image_size, resample=resample
+ )
+ split_image_arrays.extend(split_image_array)
+ image_rows.append(rows)
+ image_cols.append(cols)
+ images_split_arrays.append(split_image_arrays)
+ images_rows.append(image_rows)
+ images_cols.append(image_cols)
+ images = images_split_arrays
+ rows = images_rows
+ cols = images_cols
+ else:
+ images = [
+ [
+ self.resize(
+ image=image,
+ size=SizeDict(height=max_image_size["longest_edge"], width=max_image_size["longest_edge"]),
+ resample=resample,
+ )
+ for image in batch_images
+ ]
+ for batch_images in images
+ ]
+ rows = [[0] * len(batch_images) for batch_images in images]
+ cols = [[0] * len(batch_images) for batch_images in images]
+
+ # Rescale and normalize
+ if do_rescale:
+ images = [[self.rescale(img, rescale_factor) for img in batch_images] for batch_images in images]
+ if do_normalize:
+ images = [[self.normalize(img, image_mean, image_std) for img in batch_images] for batch_images in images]
+
+ # Pad
+ if do_pad:
+ max_num_images = max(len(images_) for images_ in images)
+ max_height, max_width = get_max_height_width(images)
+ num_channels = get_num_channels(images)
+
+ padded_images_list = [
+ [np.zeros((num_channels, max_height, max_width), dtype=np.float32) for _ in range(max_num_images)]
+ for _ in range(len(images))
+ ]
+ pixel_attention_masks = [
+ [np.zeros((max_height, max_width), dtype=np.int64) for _ in range(max_num_images)]
+ for _ in range(len(images))
+ ]
+
+ for i, batch_images in enumerate(images):
+ for j, image in enumerate(batch_images):
+ padded_images_list[i][j], pixel_attention_masks[i][j] = self.pad(image, (max_height, max_width))
+ images = padded_images_list
+
+ if do_pad:
+ data = {
+ "pixel_values": np.array(images),
+ "pixel_attention_mask": np.array(pixel_attention_masks),
+ }
+ elif return_tensors == "pt":
+ data = {"pixel_values": np.asarray(images)}
+ else:
+ data = {"pixel_values": images}
+
+ encoding = BatchFeature(data=data, tensor_type=return_tensors)
+ if return_row_col_info:
+ encoding["rows"] = rows
+ encoding["cols"] = cols
+
+ return encoding
+
+ def to_dict(self):
+ encoder_dict = super().to_dict()
+ encoder_dict.pop("_valid_processor_keys", None)
+ encoder_dict.pop("return_row_col_info", None)
+ return encoder_dict
+
+ def get_number_of_image_patches(self, height: int, width: int, images_kwargs: dict):
+ """
+ A utility that returns number of image patches for a given image size.
+
+ Args:
+ height (`int`):
+ Height of the input image.
+ width (`int`):
+ Width of the input image.
+ images_kwargs (`dict`)
+ Any kwargs to override defaults of the image processor.
+ Returns:
+ `int`: Number of patches per image.
+ """
+ do_image_splitting = images_kwargs.get("do_image_splitting", self.do_image_splitting)
+ max_image_size = images_kwargs.get("max_image_size", self.max_image_size)
+ size = images_kwargs.get("size", self.size)
+
+ num_patches = num_rows = num_cols = 0
+ if do_image_splitting:
+ height, width = _resize_output_size_rescale_to_max_len(height, width, max_len=size["longest_edge"])
+ height, width = _resize_output_size_scale_below_upper_bound(height, width, max_len=MAX_IMAGE_SIZE)
+ aspect_ratio = width / height
+
+ if width >= height:
+ resized_width = math.ceil(width / max_image_size["longest_edge"]) * max_image_size["longest_edge"]
+ resized_height = int(width / aspect_ratio)
+ resized_height = math.ceil(height / max_image_size["longest_edge"]) * max_image_size["longest_edge"]
+ elif height > width:
+ resized_height = math.ceil(height / max_image_size["longest_edge"]) * max_image_size["longest_edge"]
+ resized_width = int(height * aspect_ratio)
+ resized_width = math.ceil(width / max_image_size["longest_edge"]) * max_image_size["longest_edge"]
+
+ max_height = max_width = max_image_size["longest_edge"]
+ if resized_height > max_height or resized_width > max_width:
+ num_rows = math.ceil(resized_height / max_height)
+ num_cols = math.ceil(resized_width / max_width)
+ num_patches = num_rows * num_cols + 1
+
+ return num_patches, num_rows, num_cols
+
+
+__all__ = ["Idefics3ImageProcessorPil"]
diff --git a/third_party/transformers/src/transformers/models/idefics3/modeling_idefics3.py b/third_party/transformers/src/transformers/models/idefics3/modeling_idefics3.py
new file mode 100644
index 0000000000000000000000000000000000000000..a5d2b381c8314e288f178006d85b7e3085c259c7
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/idefics3/modeling_idefics3.py
@@ -0,0 +1,912 @@
+# Copyright 2024 the HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""PyTorch Idefics3 model."""
+
+from collections.abc import Callable
+from dataclasses import dataclass
+
+import torch
+from torch import nn
+
+from ...activations import ACT2FN
+from ...cache_utils import Cache, DynamicCache
+from ...generation import GenerationMixin
+from ...masking_utils import create_bidirectional_mask
+from ...modeling_flash_attention_utils import FlashAttentionKwargs
+from ...modeling_layers import GradientCheckpointingLayer
+from ...modeling_outputs import BaseModelOutput, BaseModelOutputWithPooling, ModelOutput
+from ...modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
+from ...processing_utils import Unpack
+from ...utils import TransformersKwargs, auto_docstring, can_return_tuple, logging
+from ...utils.generic import merge_with_config_defaults
+from ...utils.output_capturing import capture_outputs
+from ..auto import AutoModel
+from .configuration_idefics3 import Idefics3Config, Idefics3VisionConfig
+
+
+logger = logging.get_logger(__name__)
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ Base class for Idefics3 model's outputs that may also contain a past key/values (to speed up sequential decoding).
+ """
+)
+class Idefics3BaseModelOutputWithPast(ModelOutput):
+ r"""
+ last_hidden_state (`torch.FloatTensor` of shape `(batch_size, sequence_length, hidden_size)`):
+ Sequence of hidden-states at the output of the last layer of the model.
+ If `past_key_values` is used only the last hidden-state of the sequences of shape `(batch_size, 1,
+ hidden_size)` is output.
+ past_key_values (`Cache`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
+ It is a [`~cache_utils.Cache`] instance. For more details, see our [kv cache guide](https://huggingface.co/docs/transformers/en/kv_cache).
+
+ Contains pre-computed hidden-states (key and values in the self-attention blocks and optionally if
+ `config.is_encoder_decoder=True` in the cross-attention blocks) that can be used (see `past_key_values`
+ input) to speed up sequential decoding.
+ image_hidden_states (`tuple(torch.FloatTensor)`, *optional*):
+ Tuple of `torch.FloatTensor` (one for the output of the image embeddings, `(batch_size, num_images,
+ sequence_length, hidden_size)`.
+ image_hidden_states of the model produced by the vision encoder
+ """
+
+ last_hidden_state: torch.FloatTensor | None = None
+ past_key_values: Cache | None = None
+ hidden_states: tuple[torch.FloatTensor] | None = None
+ attentions: tuple[torch.FloatTensor] | None = None
+ image_hidden_states: tuple[torch.FloatTensor] | None = None
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ Base class for Idefics causal language model (or autoregressive) outputs.
+ """
+)
+class Idefics3CausalLMOutputWithPast(ModelOutput):
+ r"""
+ loss (`torch.FloatTensor` of shape `(1,)`, *optional*, returned when `labels` is provided):
+ Language modeling loss (for next-token prediction).
+ logits (`torch.FloatTensor` of shape `(batch_size, sequence_length, config.vocab_size)`):
+ Prediction scores of the language modeling head (scores for each vocabulary token before SoftMax).
+ past_key_values (`Cache`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
+ It is a [`~cache_utils.Cache`] instance. For more details, see our [kv cache guide](https://huggingface.co/docs/transformers/en/kv_cache).
+
+ Contains pre-computed hidden-states (key and values in the self-attention blocks) that can be used (see
+ `past_key_values` input) to speed up sequential decoding.
+ image_hidden_states (`tuple(torch.FloatTensor)`, *optional*):
+ Tuple of `torch.FloatTensor` (one for the output of the image embeddings, `(batch_size, num_images,
+ sequence_length, hidden_size)`.
+ image_hidden_states of the model produced by the vision encoder
+ """
+
+ loss: torch.FloatTensor | None = None
+ logits: torch.FloatTensor | None = None
+ past_key_values: Cache | None = None
+ hidden_states: tuple[torch.FloatTensor] | None = None
+ attentions: tuple[torch.FloatTensor] | None = None
+ image_hidden_states: tuple[torch.FloatTensor] | None = None
+
+
+# Copied from transformers.models.idefics2.modeling_idefics2.Idefics2VisionEmbeddings with Idefics2->Idefics3
+class Idefics3VisionEmbeddings(nn.Module):
+ """
+ This is a modified version of `siglip.modelign_siglip.SiglipVisionEmbeddings` to enable images of variable
+ resolution.
+
+ The modifications are adapted from [Patch n' Pack: NaViT, a Vision Transformer for any Aspect Ratio and Resolution](https://huggingface.co/papers/2307.06304)
+ which allows treating images in their native aspect ratio and without the need to resize them to the same
+ fixed size. In particular, we start from the original pre-trained SigLIP model
+ (which uses images of fixed-size square images) and adapt it by training on images of variable resolutions.
+ """
+
+ def __init__(self, config: Idefics3VisionConfig):
+ super().__init__()
+ self.embed_dim = config.hidden_size
+ self.image_size = config.image_size
+ self.patch_size = config.patch_size
+
+ self.patch_embedding = nn.Conv2d(
+ in_channels=config.num_channels,
+ out_channels=self.embed_dim,
+ kernel_size=self.patch_size,
+ stride=self.patch_size,
+ padding="valid",
+ )
+
+ self.num_patches_per_side = self.image_size // self.patch_size
+ self.num_patches = self.num_patches_per_side**2
+ self.num_positions = self.num_patches
+ self.position_embedding = nn.Embedding(self.num_positions, self.embed_dim)
+
+ def forward(self, pixel_values: torch.FloatTensor, patch_attention_mask: torch.BoolTensor) -> torch.Tensor:
+ batch_size, _, max_im_h, max_im_w = pixel_values.shape
+
+ patch_embeds = self.patch_embedding(pixel_values)
+ embeddings = patch_embeds.flatten(2).transpose(1, 2)
+
+ max_nb_patches_h, max_nb_patches_w = max_im_h // self.patch_size, max_im_w // self.patch_size
+ boundaries = torch.arange(
+ 1 / self.num_patches_per_side, 1.0, 1 / self.num_patches_per_side, device=pixel_values.device
+ )
+ position_ids = torch.full(
+ size=(batch_size, max_nb_patches_h * max_nb_patches_w), fill_value=0, device=pixel_values.device
+ )
+
+ nb_patches_h = patch_attention_mask[:, :, 0].sum(dim=1) # (batch_size,)
+ nb_patches_w = patch_attention_mask[:, 0, :].sum(dim=1) # (batch_size,)
+
+ step_h = 1.0 / nb_patches_h # (batch_size,)
+ step_w = 1.0 / nb_patches_w # (batch_size,)
+
+ max_patches_h = patch_attention_mask.size(1)
+ max_patches_w = patch_attention_mask.size(2)
+ h_indices = torch.arange(max_patches_h, device=position_ids.device, dtype=torch.float32)
+ w_indices = torch.arange(max_patches_w, device=position_ids.device, dtype=torch.float32)
+
+ fractional_coords_h = h_indices[None, :] * step_h[:, None]
+ fractional_coords_w = w_indices[None, :] * step_w[:, None]
+
+ fractional_coords_h = torch.clamp(fractional_coords_h, max=(1.0 - 1e-6))
+ fractional_coords_w = torch.clamp(fractional_coords_w, max=(1.0 - 1e-6))
+
+ fractional_coords_h = fractional_coords_h.to(pixel_values.dtype)
+ fractional_coords_w = fractional_coords_w.to(pixel_values.dtype)
+
+ bucket_coords_h = torch.bucketize(fractional_coords_h, boundaries, right=True)
+ bucket_coords_w = torch.bucketize(fractional_coords_w, boundaries, right=True)
+
+ pos_ids = bucket_coords_h[:, :, None] * self.num_patches_per_side + bucket_coords_w[:, None, :]
+ pos_ids = pos_ids.reshape(batch_size, -1)
+
+ position_ids[patch_attention_mask.view(batch_size, -1)] = pos_ids[patch_attention_mask.view(batch_size, -1)]
+
+ embeddings = embeddings + self.position_embedding(position_ids)
+ return embeddings
+
+
+# Copied from transformers.models.siglip.modeling_siglip.eager_attention_forward
+def eager_attention_forward(
+ module: nn.Module,
+ query: torch.Tensor,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ attention_mask: torch.Tensor | None,
+ scaling: float,
+ dropout: float = 0.0,
+ **kwargs,
+):
+ attn_weights = torch.matmul(query, key.transpose(-1, -2)) * scaling
+ if attention_mask is not None:
+ attn_weights = attn_weights + attention_mask
+
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query.dtype)
+ attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
+
+ attn_output = torch.matmul(attn_weights, value)
+ attn_output = attn_output.transpose(1, 2).contiguous()
+
+ return attn_output, attn_weights
+
+
+# Copied from transformers.models.siglip.modeling_siglip.SiglipAttention with Siglip->Idefics3Vision
+class Idefics3VisionAttention(nn.Module):
+ """Multi-headed attention from 'Attention Is All You Need' paper"""
+
+ # Copied from transformers.models.clip.modeling_clip.CLIPAttention.__init__
+ def __init__(self, config):
+ super().__init__()
+ self.config = config
+ self.embed_dim = config.hidden_size
+ self.num_heads = config.num_attention_heads
+ self.head_dim = self.embed_dim // self.num_heads
+ if self.head_dim * self.num_heads != self.embed_dim:
+ raise ValueError(
+ f"embed_dim must be divisible by num_heads (got `embed_dim`: {self.embed_dim} and `num_heads`:"
+ f" {self.num_heads})."
+ )
+ self.scale = self.head_dim**-0.5
+ self.dropout = config.attention_dropout
+
+ self.k_proj = nn.Linear(self.embed_dim, self.embed_dim)
+ self.v_proj = nn.Linear(self.embed_dim, self.embed_dim)
+ self.q_proj = nn.Linear(self.embed_dim, self.embed_dim)
+ self.out_proj = nn.Linear(self.embed_dim, self.embed_dim)
+
+ # Ignore copy
+ self.is_causal = False
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ attention_mask: torch.Tensor | None = None,
+ **kwargs,
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
+ """Input shape: Batch x Time x Channel"""
+
+ batch_size, seq_length, embed_dim = hidden_states.shape
+
+ queries = self.q_proj(hidden_states)
+ keys = self.k_proj(hidden_states)
+ values = self.v_proj(hidden_states)
+
+ queries = queries.view(batch_size, seq_length, self.num_heads, self.head_dim).transpose(1, 2)
+ keys = keys.view(batch_size, seq_length, self.num_heads, self.head_dim).transpose(1, 2)
+ values = values.view(batch_size, seq_length, self.num_heads, self.head_dim).transpose(1, 2)
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ queries,
+ keys,
+ values,
+ attention_mask,
+ is_causal=self.is_causal,
+ scaling=self.scale,
+ dropout=0.0 if not self.training else self.dropout,
+ )
+
+ attn_output = attn_output.reshape(batch_size, seq_length, embed_dim).contiguous()
+ attn_output = self.out_proj(attn_output)
+
+ return attn_output, attn_weights
+
+
+# Copied from transformers.models.siglip.modeling_siglip.SiglipMLP with Siglip->Idefics3Vision
+class Idefics3VisionMLP(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+ self.config = config
+ self.activation_fn = ACT2FN[config.hidden_act]
+ self.fc1 = nn.Linear(config.hidden_size, config.intermediate_size)
+ self.fc2 = nn.Linear(config.intermediate_size, config.hidden_size)
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ hidden_states = self.fc1(hidden_states)
+ hidden_states = self.activation_fn(hidden_states)
+ hidden_states = self.fc2(hidden_states)
+ return hidden_states
+
+
+class Idefics3SimpleMLP(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+ input_size = config.vision_config.hidden_size * (config.scale_factor**2)
+ output_size = config.text_config.hidden_size
+ self.proj = nn.Linear(input_size, output_size, bias=False)
+
+ def forward(self, x):
+ return self.proj(x)
+
+
+# Copied from transformers.models.idefics2.modeling_idefics2.Idefics2EncoderLayer with Idefics2->Idefics3
+class Idefics3EncoderLayer(GradientCheckpointingLayer):
+ def __init__(self, config: Idefics3VisionConfig):
+ super().__init__()
+ self.embed_dim = config.hidden_size
+ self.self_attn = Idefics3VisionAttention(config)
+ self.layer_norm1 = nn.LayerNorm(self.embed_dim, eps=config.layer_norm_eps)
+ self.mlp = Idefics3VisionMLP(config)
+ self.layer_norm2 = nn.LayerNorm(self.embed_dim, eps=config.layer_norm_eps)
+
+ @auto_docstring
+ # Copied from transformers.models.siglip.modeling_siglip.SiglipEncoderLayer.forward
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ attention_mask: torch.Tensor,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> torch.FloatTensor:
+ residual = hidden_states
+
+ hidden_states = self.layer_norm1(hidden_states)
+ hidden_states, _ = self.self_attn(
+ hidden_states=hidden_states,
+ attention_mask=attention_mask,
+ **kwargs,
+ )
+ hidden_states = residual + hidden_states
+
+ residual = hidden_states
+ hidden_states = self.layer_norm2(hidden_states)
+ hidden_states = self.mlp(hidden_states)
+ hidden_states = residual + hidden_states
+
+ return hidden_states
+
+
+# Copied from transformers.models.siglip.modeling_siglip.SiglipEncoder with Siglip->Idefics3
+class Idefics3Encoder(nn.Module):
+ """
+ Transformer encoder consisting of `config.num_hidden_layers` self attention layers. Each layer is a
+ [`Idefics3EncoderLayer`].
+
+ Args:
+ config: Idefics3Config
+ """
+
+ def __init__(self, config: Idefics3Config):
+ super().__init__()
+ self.config = config
+ self.layers = nn.ModuleList([Idefics3EncoderLayer(config) for _ in range(config.num_hidden_layers)])
+ self.gradient_checkpointing = False
+
+ # Ignore copy
+ @auto_docstring
+ def forward(
+ self,
+ inputs_embeds,
+ attention_mask: torch.Tensor | None = None,
+ ) -> tuple | BaseModelOutput:
+ hidden_states = inputs_embeds
+ for encoder_layer in self.layers:
+ layer_outputs = encoder_layer(
+ hidden_states,
+ attention_mask,
+ )
+
+ hidden_states = layer_outputs
+
+ return BaseModelOutput(last_hidden_state=hidden_states)
+
+
+# Copied from transformers.models.llama.modeling_llama.repeat_kv
+def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
+ """
+ This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
+ num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
+ """
+ batch, num_key_value_heads, slen, head_dim = hidden_states.shape
+ if n_rep == 1:
+ return hidden_states
+ hidden_states = hidden_states[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim)
+ return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
+
+
+# Copied from transformers.models.llama.modeling_llama.LlamaRMSNorm with Llama->Idefics3
+class Idefics3RMSNorm(nn.Module):
+ def __init__(self, hidden_size, eps: float = 1e-6) -> None:
+ """
+ Idefics3RMSNorm is equivalent to T5LayerNorm
+ """
+ super().__init__()
+ self.weight = nn.Parameter(torch.ones(hidden_size))
+ self.variance_epsilon = eps
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ input_dtype = hidden_states.dtype
+ hidden_states = hidden_states.to(torch.float32)
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
+ hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
+ return self.weight * hidden_states.to(input_dtype)
+
+ def extra_repr(self):
+ return f"{tuple(self.weight.shape)}, eps={self.variance_epsilon}"
+
+
+class Idefics3Connector(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+ self.scale_factor = config.scale_factor
+ self.modality_projection = Idefics3SimpleMLP(config)
+
+ def pixel_shuffle(self, x, scale_factor=2):
+ bsz, seq, embed_dim = x.size()
+ height = width = int(seq**0.5)
+ x = x.view(bsz, height, width, embed_dim)
+ x = x.view(bsz, height, int(width / scale_factor), embed_dim * scale_factor)
+ x = x.permute(0, 2, 1, 3)
+ x = x.reshape(bsz, int(width / scale_factor), int(height / scale_factor), embed_dim * (scale_factor**2))
+ x = x.permute(0, 2, 1, 3)
+ x = x.reshape(bsz, int(seq / (scale_factor**2)), embed_dim * (scale_factor**2))
+ return x
+
+ def forward(self, image_hidden_states):
+ image_hidden_states = self.pixel_shuffle(image_hidden_states, self.scale_factor)
+ image_hidden_states = self.modality_projection(image_hidden_states)
+ return image_hidden_states
+
+
+@auto_docstring
+class Idefics3PreTrainedModel(PreTrainedModel):
+ config: Idefics3Config
+ base_model_prefix = "model"
+ input_modalities = ("image", "text")
+ supports_gradient_checkpointing = True
+ _no_split_modules = ["Idefics3VisionAttention", "Idefics3DecoderLayer"]
+ _skip_keys_device_placement = "past_key_values"
+ _supports_flash_attn = True
+ _supports_sdpa = True
+ _supports_flex_attn = True
+ _supports_attention_backend = True
+
+
+@auto_docstring(
+ custom_intro="""
+ The Idefics3 Vision Transformer Model outputting raw image embedding.
+ """
+)
+class Idefics3VisionTransformer(Idefics3PreTrainedModel):
+ config: Idefics3VisionConfig
+ input_modalities = ("image",)
+ _can_record_outputs = {
+ "hidden_states": Idefics3EncoderLayer,
+ "attentions": Idefics3VisionAttention,
+ }
+
+ def __init__(self, config: Idefics3VisionConfig):
+ super().__init__(config)
+ embed_dim = config.hidden_size
+
+ self.embeddings = Idefics3VisionEmbeddings(config)
+ self.encoder = Idefics3Encoder(config)
+ self.patch_size = config.patch_size
+ self.post_layernorm = nn.LayerNorm(embed_dim, eps=config.layer_norm_eps)
+
+ self.post_init()
+
+ # Copied from transformers.models.idefics2.modeling_idefics2.Idefics2VisionTransformer.get_input_embeddings
+ def get_input_embeddings(self):
+ return self.embeddings
+
+ # Copied from transformers.models.idefics2.modeling_idefics2.Idefics2VisionTransformer.set_input_embeddings
+ def set_input_embeddings(self, value):
+ self.embeddings = value
+
+ @merge_with_config_defaults
+ @capture_outputs(tie_last_hidden_states=False)
+ def forward(
+ self,
+ pixel_values,
+ patch_attention_mask: torch.BoolTensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | BaseModelOutput:
+ batch_size = pixel_values.size(0)
+ if patch_attention_mask is None:
+ patch_size = self.patch_size
+ patch_attention_mask = torch.ones(
+ (
+ batch_size,
+ pixel_values.size(2) // patch_size,
+ pixel_values.size(3) // patch_size,
+ )
+ )
+ patch_attention_mask = patch_attention_mask.to(dtype=torch.bool, device=pixel_values.device)
+
+ hidden_states = self.embeddings(pixel_values=pixel_values, patch_attention_mask=patch_attention_mask)
+
+ patch_attention_mask = patch_attention_mask.view(batch_size, -1)
+ # Create the correct attention mask based on the attention implementation
+ patch_attention_mask = create_bidirectional_mask(
+ config=self.config,
+ inputs_embeds=hidden_states,
+ attention_mask=patch_attention_mask,
+ )
+
+ encoder_outputs: BaseModelOutput = self.encoder(
+ inputs_embeds=hidden_states,
+ attention_mask=patch_attention_mask,
+ )
+
+ last_hidden_state = encoder_outputs.last_hidden_state
+ last_hidden_state = self.post_layernorm(last_hidden_state)
+
+ return BaseModelOutput(
+ last_hidden_state=last_hidden_state,
+ )
+
+
+@auto_docstring(
+ custom_intro="""
+ Idefics3 model consisting of a SIGLIP vision encoder and Llama3 language decoder
+ """
+)
+class Idefics3Model(Idefics3PreTrainedModel):
+ def __init__(self, config: Idefics3Config):
+ super().__init__(config)
+ self.padding_idx = self.config.text_config.pad_token_id
+ self.vocab_size = self.config.text_config.vocab_size
+
+ self.vision_model = Idefics3VisionTransformer._from_config(config.vision_config)
+ self.connector = Idefics3Connector(config)
+ self.text_model = AutoModel.from_config(config.text_config)
+
+ self.image_seq_len = int(
+ ((config.vision_config.image_size // config.vision_config.patch_size) ** 2) / (config.scale_factor**2)
+ )
+ self.image_token_id = self.config.image_token_id
+
+ self.post_init()
+
+ # Copied from transformers.models.idefics2.modeling_idefics2.Idefics2Model.get_input_embeddings
+ def get_input_embeddings(self):
+ return self.text_model.get_input_embeddings()
+
+ # Copied from transformers.models.idefics2.modeling_idefics2.Idefics2Model.set_input_embeddings
+ def set_input_embeddings(self, value):
+ self.text_model.set_input_embeddings(value)
+
+ def inputs_merger(
+ self,
+ input_ids: torch.LongTensor,
+ inputs_embeds: torch.Tensor | None,
+ image_hidden_states: torch.Tensor | None,
+ ):
+ """
+ This method aims at merging the token embeddings with the image hidden states into one single sequence of vectors that are fed to the transformer LM.
+ The merging happens as follows:
+ - The text token sequence is: `tok_1 tok_2 tok_3 ... tok_4`.
+ - We get the image hidden states for the image through the vision encoder and that hidden state, after a pixel shuffle operation, is then projected into the text embedding space.
+ We thus have a sequence of image hidden states of size (1, image_seq_len, hidden_dim), where 1 is for batch_size of 1 image and hidden_dim is the hidden_dim of the LM transformer.
+ - The merging happens so that we obtain the following sequence: `vector_tok_1 vector_tok_2 vector_tok_3 vector_fake_tok_around_image {sequence of image_seq_len image hidden states} vector_fake_toke_around_image vector_tok_4`. That sequence is fed to the LM.
+ - To fit the format of that sequence, `input_ids`, `inputs_embeds`, `attention_mask` are all 3 adapted to insert the image hidden states.
+ """
+ if input_ids is None:
+ special_image_mask = inputs_embeds == self.get_input_embeddings()(
+ torch.tensor(self.config.image_token_id, dtype=torch.long, device=inputs_embeds.device)
+ )
+ special_image_mask = special_image_mask.all(-1)
+ else:
+ special_image_mask = input_ids == self.config.image_token_id
+
+ special_image_mask = special_image_mask.unsqueeze(-1).expand_as(inputs_embeds).to(inputs_embeds.device)
+ image_hidden_states = image_hidden_states.to(inputs_embeds.device, inputs_embeds.dtype)
+ inputs_embeds = inputs_embeds.masked_scatter(special_image_mask, image_hidden_states)
+ return inputs_embeds
+
+ @can_return_tuple
+ @auto_docstring
+ def get_image_features(
+ self,
+ pixel_values: torch.FloatTensor,
+ pixel_attention_mask: torch.LongTensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | BaseModelOutputWithPooling:
+ r"""
+ pixel_values (`torch.FloatTensor` of shape `(batch_size, num_channels, image_size, image_size)`):
+ The tensors corresponding to the input images.
+ pixel_attention_mask (`torch.LongTensor`, *optional*):
+ The attention mask indicating padded regions in the image.
+ """
+ batch_size, num_images, num_channels, height, width = pixel_values.shape
+ pixel_values = pixel_values.to(dtype=self.dtype) # fp16 compatibility
+ pixel_values = pixel_values.view(batch_size * num_images, *pixel_values.shape[2:])
+
+ # Remove padding images - padding images are full 0.
+ nb_values_per_image = pixel_values.shape[1:].numel()
+ real_images_inds = (pixel_values == 0.0).sum(dim=(-1, -2, -3)) != nb_values_per_image
+ pixel_values = pixel_values[real_images_inds].contiguous()
+
+ # Handle the vision attention mask
+ if pixel_attention_mask is None:
+ pixel_attention_mask = torch.ones(
+ size=(pixel_values.size(0), pixel_values.size(2), pixel_values.size(3)),
+ dtype=torch.bool,
+ device=pixel_values.device,
+ )
+ else:
+ # Remove padding images from the mask
+ pixel_attention_mask = pixel_attention_mask.view(batch_size * num_images, *pixel_attention_mask.shape[2:])
+ pixel_attention_mask = pixel_attention_mask[real_images_inds].contiguous()
+
+ patch_size = self.config.vision_config.patch_size
+ patches_subgrid = pixel_attention_mask.unfold(dimension=1, size=patch_size, step=patch_size)
+ patches_subgrid = patches_subgrid.unfold(dimension=2, size=patch_size, step=patch_size)
+ patch_attention_mask = (patches_subgrid.sum(dim=(-1, -2)) > 0).bool()
+
+ # Get sequence from the vision encoder
+ image_outputs = self.vision_model(
+ pixel_values=pixel_values, patch_attention_mask=patch_attention_mask, return_dict=True, **kwargs
+ )
+ image_hidden_states = image_outputs.last_hidden_state
+
+ # Modality projection & resampling
+ image_features = self.connector(image_hidden_states)
+ image_outputs.pooler_output = image_features
+
+ return image_outputs
+
+ @can_return_tuple
+ @auto_docstring(
+ custom_intro="""
+ Inputs fed to the model can have an arbitrary number of images. To account for this, pixel_values fed to
+ the model have image padding -> (batch_size, max_num_images, 3, max_heights, max_widths) where
+ max_num_images is the maximum number of images among the batch_size samples in the batch.
+ Padding images are not needed beyond padding the pixel_values at the entrance of the model.
+ For efficiency, we only pass through the vision_model's forward the real images by
+ discarding the padding images i.e. pixel_values of size (image_batch_size, 3, height, width) where
+ image_batch_size would be 7 when num_images_per_sample=[1, 3, 1, 2] and max_num_images would be 3.
+ """
+ )
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ pixel_values: torch.FloatTensor | None = None,
+ pixel_attention_mask: torch.BoolTensor | None = None,
+ image_hidden_states: torch.FloatTensor | None = None,
+ use_cache: bool | None = None,
+ **kwargs: Unpack[FlashAttentionKwargs],
+ ) -> tuple | Idefics3BaseModelOutputWithPast:
+ r"""
+ pixel_attention_mask (`torch.Tensor` of shape `(batch_size, image_size, image_size)`, *optional*):
+ Mask to avoid performing attention on padding pixel indices.
+ image_hidden_states (`torch.FloatTensor` of shape `(batch_size, num_channels, image_size, image_size)`):
+ The hidden states of the image encoder after modality projection.
+ """
+
+ if self.training and self.text_model.gradient_checkpointing and use_cache:
+ logger.warning_once(
+ "`use_cache=True` is incompatible with gradient checkpointing. Setting `use_cache=False`..."
+ )
+ use_cache = False
+
+ # retrieve input_ids and inputs_embeds
+ if input_ids is not None:
+ batch_size, seq_length = input_ids.shape
+ elif inputs_embeds is not None:
+ batch_size, seq_length, _ = inputs_embeds.shape
+ else:
+ raise ValueError("You have to specify either input_ids or inputs_embeds")
+
+ if use_cache and past_key_values is None:
+ past_key_values = DynamicCache(config=self.config)
+
+ if inputs_embeds is None:
+ inputs_embeds = self.text_model.get_input_embeddings()(input_ids).to(self.device)
+
+ # START VISUAL INPUTS INTEGRATION
+ if pixel_values is not None and image_hidden_states is not None:
+ raise ValueError("You cannot specify both pixel_values and image_hidden_states at the same time")
+ elif pixel_values is not None:
+ image_hidden_states = self.get_image_features(
+ pixel_values, pixel_attention_mask, return_dict=True
+ ).pooler_output
+ elif image_hidden_states is not None:
+ image_hidden_states = image_hidden_states.to(dtype=self.dtype, device=input_ids.device)
+
+ if image_hidden_states is not None:
+ # When we generate, we don't want to replace the potential image_token_id that we generated by images
+ # that simply don't exist
+ inputs_embeds = self.inputs_merger(
+ input_ids=input_ids,
+ inputs_embeds=inputs_embeds,
+ image_hidden_states=image_hidden_states,
+ )
+
+ outputs = self.text_model(
+ inputs_embeds=inputs_embeds,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ return Idefics3BaseModelOutputWithPast(
+ last_hidden_state=outputs.last_hidden_state,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ image_hidden_states=image_hidden_states,
+ )
+
+
+@auto_docstring(
+ custom_intro="""
+ The Idefics3 Model with a language modeling head. It is made up a SigLIP vision encoder, with a language modeling head on top.
+ """
+)
+class Idefics3ForConditionalGeneration(Idefics3PreTrainedModel, GenerationMixin):
+ _tied_weights_keys = {"lm_head.weight": "model.text_model.embed_tokens.weight"}
+
+ # Copied from transformers.models.idefics2.modeling_idefics2.Idefics2ForConditionalGeneration.__init__ with Idefics2->Idefics3
+ def __init__(self, config):
+ super().__init__(config)
+ self.model = Idefics3Model(config)
+ self.image_token_id = self.config.image_token_id
+
+ self.lm_head = nn.Linear(config.text_config.hidden_size, config.text_config.vocab_size, bias=False)
+ self.vocab_size = config.text_config.vocab_size
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ # Copied from transformers.models.idefics2.modeling_idefics2.Idefics2ForConditionalGeneration.get_input_embeddings
+ def get_input_embeddings(self):
+ return self.model.text_model.get_input_embeddings()
+
+ # Copied from transformers.models.idefics2.modeling_idefics2.Idefics2ForConditionalGeneration.set_input_embeddings
+ def set_input_embeddings(self, value):
+ self.model.text_model.set_input_embeddings(value)
+
+ @auto_docstring
+ def get_image_features(
+ self,
+ pixel_values: torch.FloatTensor,
+ pixel_attention_mask: torch.LongTensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | BaseModelOutputWithPooling:
+ r"""
+ pixel_values (`torch.FloatTensor` of shape `(batch_size, num_channels, image_size, image_size)`):
+ The tensors corresponding to the input images.
+ pixel_attention_mask (`torch.LongTensor`, *optional*):
+ The attention mask indicating padded regions in the image.
+ """
+ return self.model.get_image_features(
+ pixel_values=pixel_values, pixel_attention_mask=pixel_attention_mask, **kwargs
+ )
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ pixel_values: torch.FloatTensor | None = None,
+ pixel_attention_mask: torch.BoolTensor | None = None,
+ image_hidden_states: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | Idefics3CausalLMOutputWithPast:
+ r"""
+ pixel_attention_mask (`torch.Tensor` of shape `(batch_size, image_size, image_size)`, *optional*):
+ Mask to avoid performing attention on padding pixel indices.
+ image_hidden_states (`torch.FloatTensor` of shape `(batch_size, num_channels, image_size, image_size)`):
+ The hidden states of the image encoder after modality projection.
+ 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 `model.image_token_id` (where `model` is your instance of `Idefics3ForConditionalGeneration`).
+ Tokens with indices set to `model.image_token_id` are ignored (masked), the loss is only
+ computed for the tokens with labels in `[0, ..., config.vocab_size]`.
+
+ Example:
+
+ ```python
+ >>> import torch
+ >>> from PIL import Image
+ >>> from io import BytesIO
+
+ >>> from transformers import AutoProcessor, AutoModelForImageTextToText
+ >>> from transformers.image_utils import load_image
+
+ >>> # Note that passing the image urls (instead of the actual pil images) to the processor is also possible
+ >>> image1 = load_image("https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg")
+ >>> image2 = load_image("https://cdn.britannica.com/59/94459-050-DBA42467/Skyline-Chicago.jpg")
+ >>> image3 = load_image("https://cdn.britannica.com/68/170868-050-8DDE8263/Golden-Gate-Bridge-San-Francisco.jpg")
+
+ >>> processor = AutoProcessor.from_pretrained("HuggingFaceM4/Idefics3-8B-Llama3")
+ >>> model = AutoModelForImageTextToText.from_pretrained("HuggingFaceM4/Idefics3-8B-Llama3", dtype=torch.bfloat16, device_map="auto")
+
+ >>> # Create inputs
+ >>> messages = [
+ ... {
+ ... "role": "user",
+ ... "content": [
+ ... {"type": "image"},
+ ... {"type": "text", "text": "In this image, we can see the city of New York, and more specifically the Statue of Liberty."},
+ ... {"type": "image"},
+ ... {"type": "text", "text": "What can we see in this image?"},
+ ... ]
+ ... },
+ ... {
+ ... "role": "user",
+ ... "content": [
+ ... {"type": "image"},
+ ... {"type": "text", "text": "In which city is that bridge located?"},
+ ... ]
+ ... }
+ ... ]
+
+ >>> prompts = [processor.apply_chat_template([message], add_generation_prompt=True) for message in messages]
+ >>> images = [[image1, image2], [image3]]
+ >>> inputs = processor(text=prompts, images=images, padding=True, return_tensors="pt").to(model.device)
+
+ >>> # Generate
+ >>> generated_ids = model.generate(**inputs, max_new_tokens=256)
+ >>> generated_texts = processor.batch_decode(generated_ids, skip_special_tokens=True)
+
+ >>> print(generated_texts[0])
+ Assistant: There are buildings, trees, lights, and water visible in this image.
+
+ >>> print(generated_texts[1])
+ Assistant: The bridge is in San Francisco.
+ ```"""
+ # decoder outputs consists of (dec_features, layer_state, dec_hidden, dec_attn)
+ outputs = self.model(
+ input_ids=input_ids,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ pixel_values=pixel_values,
+ pixel_attention_mask=pixel_attention_mask,
+ image_hidden_states=image_hidden_states,
+ use_cache=use_cache,
+ return_dict=True,
+ **kwargs,
+ )
+
+ hidden_states = outputs[0]
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(
+ logits=logits, labels=labels, vocab_size=self.config.text_config.vocab_size, **kwargs
+ )
+
+ return Idefics3CausalLMOutputWithPast(
+ loss=loss,
+ logits=logits,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ image_hidden_states=outputs.image_hidden_states,
+ )
+
+ # Copied from transformers.models.idefics2.modeling_idefics2.Idefics2ForConditionalGeneration.prepare_inputs_for_generation
+ def prepare_inputs_for_generation(
+ self,
+ input_ids,
+ past_key_values=None,
+ attention_mask=None,
+ inputs_embeds=None,
+ pixel_values=None,
+ pixel_attention_mask=None,
+ image_hidden_states=None,
+ logits_to_keep=None,
+ is_first_iteration=False,
+ use_cache=False,
+ **kwargs,
+ ):
+ # Overwritten -- there are mutually exclusive inputs (if the logic to make `image_hidden_states` take
+ # precedence is moved to the model, we can remove this fn)
+
+ model_inputs = super().prepare_inputs_for_generation(
+ input_ids,
+ past_key_values=past_key_values,
+ attention_mask=attention_mask,
+ inputs_embeds=inputs_embeds,
+ pixel_values=pixel_values,
+ pixel_attention_mask=pixel_attention_mask,
+ image_hidden_states=image_hidden_states,
+ logits_to_keep=logits_to_keep,
+ is_first_iteration=is_first_iteration,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ if image_hidden_states is not None or (use_cache and not is_first_iteration):
+ model_inputs["pixel_values"] = None
+ model_inputs["pixel_attention_mask"] = None
+
+ return model_inputs
+
+
+__all__ = ["Idefics3ForConditionalGeneration", "Idefics3PreTrainedModel", "Idefics3Model", "Idefics3VisionTransformer"]
diff --git a/third_party/transformers/src/transformers/models/idefics3/processing_idefics3.py b/third_party/transformers/src/transformers/models/idefics3/processing_idefics3.py
new file mode 100644
index 0000000000000000000000000000000000000000..f43ac76bf3ffeae21387318be6a07f373800b97d
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/idefics3/processing_idefics3.py
@@ -0,0 +1,344 @@
+# Copyright 2024 The HuggingFace Inc. team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Processor class for Idefics3.
+"""
+
+import re
+from itertools import accumulate
+from typing import TYPE_CHECKING, Union
+
+import numpy as np
+
+from ...feature_extraction_utils import BatchFeature
+from ...image_utils import ImageInput, is_valid_image, load_image
+from ...processing_utils import MultiModalData, ProcessingKwargs, ProcessorMixin, Unpack
+from ...tokenization_utils_base import AddedToken, BatchEncoding, TextInput
+from ...utils import auto_docstring, logging
+
+
+if TYPE_CHECKING:
+ from ...tokenization_utils_base import PreTokenizedInput
+
+logger = logging.get_logger(__name__)
+
+
+def is_url(val) -> bool:
+ return isinstance(val, str) and val.startswith("http")
+
+
+def is_image_or_image_url(elem):
+ return is_url(elem) or is_valid_image(elem)
+
+
+def _prompt_split_image(image_seq_len, image_rows, image_cols, fake_token_around_image, image_token, global_img_token):
+ """Prompt with expanded image tokens for when the image is split into patches."""
+ text_split_images = ""
+ for n_h in range(image_rows):
+ for n_w in range(image_cols):
+ text_split_images += (
+ f"{fake_token_around_image}" + f"" + f"{image_token}" * image_seq_len
+ )
+ text_split_images += "\n"
+
+ text_split_images += (
+ f"\n{fake_token_around_image}"
+ + f"{global_img_token}"
+ + f"{image_token}" * image_seq_len
+ + f"{fake_token_around_image}"
+ )
+ return text_split_images
+
+
+def _prompt_single_image(image_seq_len, fake_token_around_image, image_token, global_img_token):
+ """Prompt with expanded image tokens for a single image."""
+ return (
+ f"{fake_token_around_image}"
+ + f"{global_img_token}"
+ + f"{image_token}" * image_seq_len
+ + f"{fake_token_around_image}"
+ )
+
+
+def get_image_prompt_string(
+ image_rows, image_cols, image_seq_len, fake_token_around_image, image_token, global_img_token
+):
+ if image_rows == 0 and image_cols == 0:
+ return _prompt_single_image(
+ image_seq_len,
+ fake_token_around_image=fake_token_around_image,
+ image_token=image_token,
+ global_img_token=global_img_token,
+ )
+ return _prompt_split_image(
+ image_seq_len, image_rows, image_cols, fake_token_around_image, image_token, global_img_token
+ )
+
+
+class Idefics3ProcessorKwargs(ProcessingKwargs, total=False):
+ _defaults = {
+ "text_kwargs": {
+ "add_special_tokens": True,
+ "padding": False,
+ "is_split_into_words": False,
+ "return_mm_token_type_ids": False,
+ },
+ "images_kwargs": {
+ "return_row_col_info": True,
+ },
+ }
+
+
+@auto_docstring
+class Idefics3Processor(ProcessorMixin):
+ def __init__(
+ self, image_processor, tokenizer=None, image_seq_len: int = 169, chat_template: str | None = None, **kwargs
+ ):
+ r"""
+ image_seq_len (`int`, *optional*, defaults to 169):
+ The length of the image sequence i.e. the number of tokens per image in the input.
+ This parameter is used to build the string from the input prompt and image tokens and should match the
+ value the model used. It is computed as: image_seq_len = int(((image_size // patch_size) ** 2) / (scale_factor**2))
+ """
+ self.fake_image_token = AddedToken("", normalized=False, special=True).content
+ self.image_token = AddedToken("", normalized=False, special=True).content
+ self.end_of_utterance_token = AddedToken("", normalized=False, special=True).content
+ self.global_image_tag = "" # https://github.com/huggingface/transformers/pull/32473/files/8063e5e17362571b693f1db95167f5443a3be1b2#r1734825341
+ self.image_seq_len = image_seq_len
+ self.image_token_id = tokenizer.convert_tokens_to_ids(self.image_token)
+ self.fake_image_token_id = tokenizer.convert_tokens_to_ids(self.fake_image_token)
+ self.global_image_token_id = tokenizer.convert_tokens_to_ids(self.global_image_tag)
+ self.row_col_ids = [
+ tokenizer.convert_tokens_to_ids(f"") for i in range(6) for j in range(6)
+ ]
+
+ # This regex matches one or more occurrences of tags (optionally surrounded by newline characters)
+ # or tags (where x and y are digits, also optionally surrounded by newline characters).
+ self._regex_to_remove_extra_special_tokens = re.compile(r"(\n?\n?|\n?)+")
+
+ tokens_to_add = {
+ "additional_special_tokens": [
+ self.fake_image_token,
+ self.image_token,
+ self.end_of_utterance_token,
+ ]
+ }
+ tokenizer.add_special_tokens(tokens_to_add)
+ self.image_token_id = tokenizer.convert_tokens_to_ids(self.image_token)
+
+ super().__init__(image_processor, tokenizer, chat_template=chat_template, **kwargs)
+
+ def _extract_images_from_prompts(self, prompts):
+ prompt_images = []
+ for prompt in prompts:
+ images = []
+ for elem in prompt:
+ if is_valid_image(elem):
+ images.append(elem)
+ elif is_url(elem):
+ images.append(load_image(elem))
+ prompt_images.append(images)
+ return prompt_images
+
+ @auto_docstring
+ def __call__(
+ self,
+ images: ImageInput | list[ImageInput] | list[list[ImageInput]] = None,
+ text: Union[TextInput, "PreTokenizedInput", list[TextInput], list["PreTokenizedInput"]] = None,
+ image_seq_len: int | None = None,
+ **kwargs: Unpack[Idefics3ProcessorKwargs],
+ ) -> BatchEncoding:
+ r"""
+ image_seq_len (`int`, *optional*):
+ The length of the image sequence. If not provided, the default value of self.image_seq_len is used.
+ image_seq_len should be equal to int(((image_size // patch_size) ** 2) / (scale_factor**2))
+ """
+ if text is None and images is None:
+ raise ValueError("You must provide either `text` or `images`.")
+
+ output_kwargs = self._merge_kwargs(
+ Idefics3ProcessorKwargs,
+ tokenizer_init_kwargs=self.tokenizer.init_kwargs,
+ **kwargs,
+ )
+
+ image_seq_len = image_seq_len if image_seq_len is not None else self.image_seq_len
+ return_mm_token_type_ids = output_kwargs["text_kwargs"].pop("return_mm_token_type_ids", False)
+ return_tensors = output_kwargs["text_kwargs"].pop("return_tensors", None)
+
+ n_images_in_text = []
+ n_images_in_images = []
+ inputs = {}
+
+ if text is not None:
+ if isinstance(text, str):
+ text = [text]
+ elif not isinstance(text, list) and not isinstance(text[0], str):
+ raise ValueError("Invalid input text. Please provide a string, or a list of strings")
+ n_images_in_text = [sample.count(self.image_token) for sample in text]
+
+ if images is not None:
+ if is_image_or_image_url(images):
+ images = [[images]]
+ elif isinstance(images, (list, tuple)) and is_image_or_image_url(images[0]):
+ if text is not None:
+ if sum(n_images_in_text) != len(images):
+ raise ValueError(
+ f"The total number of {self.image_token} tokens in the prompts should be the same as the number of images passed."
+ f" Found {sum(n_images_in_text)} {self.image_token} tokens and {len(images)} images."
+ )
+ # Reorganize the images to match the prompts
+ cumsum_images_in_text = [0] + list(accumulate(n_images_in_text))
+ images = [
+ images[cumsum_images_in_text[i] : cumsum_images_in_text[i + 1]]
+ for i in range(len(n_images_in_text))
+ ]
+ else:
+ images = [images]
+ elif (
+ not isinstance(images, (list, tuple))
+ and not isinstance(images[0], (list, tuple))
+ and not is_image_or_image_url(images[0][0])
+ ):
+ raise ValueError(
+ "Invalid input images. Please provide a single image or a list of images or a list of list of images."
+ )
+ n_images_in_images = [len(sample) for sample in images]
+
+ # Load images if they are URLs
+ images = [[load_image(im) if is_url(im) else im for im in sample] for sample in images]
+
+ image_inputs = self.image_processor(images, **output_kwargs["images_kwargs"])
+ inputs.update(image_inputs)
+
+ if text is not None:
+ if n_images_in_images != n_images_in_text:
+ raise ValueError(
+ f"The number of images in the text {n_images_in_text} and images {n_images_in_images} should be the same."
+ )
+
+ image_rows = inputs.pop("rows", [[0] * n_images for n_images in n_images_in_text])
+ image_cols = inputs.pop("cols", [[0] * n_images for n_images in n_images_in_text])
+
+ fake_image_token = self.fake_image_token
+ image_token = self.image_token
+ global_img_token = self.global_image_tag
+
+ prompt_strings = []
+ batch_image_seq_lengths = []
+ for sample, sample_rows, sample_cols in zip(text, image_rows, image_cols):
+ # Replace the image token with fake tokens around the expanded image token sequence of length `image_seq_len`
+ image_prompt_strings = []
+ image_seq_lengths = []
+ for n_rows, n_cols in zip(sample_rows, sample_cols):
+ image_prompt_string = get_image_prompt_string(
+ n_rows,
+ n_cols,
+ image_seq_len,
+ image_token=image_token,
+ fake_token_around_image=fake_image_token,
+ global_img_token=global_img_token,
+ )
+ # Add +2 and +3 for special BOI/EOI/fake_image_wrapper tokens
+ row_length = (self.image_seq_len + 2) * n_cols + 1
+ image_seq_lengths.append((self.image_seq_len + 3) + row_length * n_rows)
+ image_prompt_strings.append(image_prompt_string)
+
+ batch_image_seq_lengths.append(image_seq_lengths)
+ split_sample = sample.split(image_token)
+ if len(split_sample) == 0:
+ raise ValueError("The image token should be present in the text.")
+
+ # Place in the image prompt strings where the image tokens are
+ sample = split_sample[0]
+ for i, image_prompt_string in enumerate(image_prompt_strings):
+ sample += image_prompt_string + split_sample[i + 1]
+ prompt_strings.append(sample)
+
+ text_inputs = self.tokenizer(prompt_strings, **output_kwargs["text_kwargs"])
+ self._check_special_mm_tokens(prompt_strings, text_inputs, modalities=["image"])
+ inputs.update(text_inputs)
+
+ elif text is not None:
+ if any(n_images_in_text):
+ raise ValueError(
+ f"Found {sum(n_images_in_text)} {self.image_token} tokens in the text but no images were passed."
+ )
+ text_inputs = self.tokenizer(text=text, **output_kwargs["text_kwargs"])
+ inputs.update(text_inputs)
+
+ if return_mm_token_type_ids:
+ inputs["mm_token_type_ids"] = self.create_mm_token_type_ids(inputs["input_ids"], batch_image_seq_lengths)
+ return BatchFeature(data=inputs, tensor_type=return_tensors)
+
+ def create_mm_token_type_ids(self, input_ids: list, batch_image_seq_lengths: list[int]) -> list[list[int]]:
+ # We have to iterate for each list separately because inputs
+ # might be non-padded lists and we can't cast numpy on that!
+ # Then cast numpy as each input for faster indexing
+ mm_token_type_ids = []
+ for i, seq_lengths in enumerate(batch_image_seq_lengths):
+ array_ids = np.array(input_ids[i])
+ mm_token_types = np.zeros_like(array_ids)
+ image_start_positions = np.where(array_ids == self.fake_image_token_id)[0]
+ j = 0
+ for seq_len in seq_lengths:
+ if j >= len(image_start_positions):
+ break
+ start = image_start_positions[j]
+ end = start + seq_len
+ mm_token_types[start:end] = 1
+ j = np.searchsorted(image_start_positions, end)
+ mm_token_type_ids.append(mm_token_types.tolist())
+
+ return mm_token_type_ids
+
+ def _get_num_multimodal_tokens(self, image_sizes=None, **kwargs):
+ """
+ Computes the number of placeholder tokens needed for multimodal inputs with the given sizes.
+
+ Args:
+ image_sizes (`list[list[int]]`, *optional*):
+ The input sizes formatted as (height, width) per each image.
+
+ Returns:
+ `MultiModalData`: A `MultiModalData` object holding number of tokens per each of the provided
+ input modalities, along with other useful data.
+ """
+
+ vision_data = {}
+ if image_sizes is not None:
+ images_kwargs = Idefics3ProcessorKwargs._defaults.get("images_kwargs", {})
+ images_kwargs.update(kwargs)
+
+ num_image_row_cols = [
+ self.image_processor.get_number_of_image_patches(*image_size, images_kwargs)
+ for image_size in image_sizes
+ ]
+
+ base_image_length = self.image_seq_len + 3
+ col_length = self.image_seq_len + 2
+ num_image_tokens = []
+ num_image_patches = []
+
+ for num_patches, num_rows, num_cols in num_image_row_cols:
+ row_length = col_length * num_cols + 1
+ num_image_tokens.append(base_image_length + (row_length * num_rows))
+ num_image_patches.append(num_patches)
+
+ vision_data.update({"num_image_tokens": num_image_tokens, "num_image_patches": num_image_patches})
+
+ return MultiModalData(**vision_data)
+
+
+__all__ = ["Idefics3Processor"]
diff --git a/third_party/transformers/src/transformers/models/ijepa/__init__.py b/third_party/transformers/src/transformers/models/ijepa/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..8923af1de116219405577646ae2dcedee5602ccc
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/ijepa/__init__.py
@@ -0,0 +1,27 @@
+# Copyright 2024 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_ijepa import *
+ from .modeling_ijepa import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/ijepa/configuration_ijepa.py b/third_party/transformers/src/transformers/models/ijepa/configuration_ijepa.py
new file mode 100644
index 0000000000000000000000000000000000000000..bed2e63b3ae113d7dcd741c245abc7b84581076f
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/ijepa/configuration_ijepa.py
@@ -0,0 +1,69 @@
+# Copyright 2024 The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""I-JEPA model configuration"""
+
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...utils import auto_docstring
+
+
+@auto_docstring(checkpoint="facebook/ijepa_vith14_1k")
+@strict
+class IJepaConfig(PreTrainedConfig):
+ r"""
+ pooler_output_size (`int`, *optional*):
+ Dimensionality of the pooler layer. If None, defaults to `hidden_size`.
+ pooler_act (`str`, *optional*, defaults to `"tanh"`):
+ The activation function to be used by the pooler.
+
+ Example:
+
+ ```python
+ >>> from transformers import IJepaConfig, IJepaModel
+
+ >>> # Initializing a IJEPA ijepa-base-patch16-224 style configuration
+ >>> configuration = IJepaConfig()
+
+ >>> # Initializing a model (with random weights) from the ijepa-base-patch16-224 style configuration
+ >>> model = IJepaModel(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "ijepa"
+
+ hidden_size: int = 768
+ num_hidden_layers: int = 12
+ num_attention_heads: int = 12
+ intermediate_size: int = 3072
+ hidden_act: str = "gelu"
+ hidden_dropout_prob: float | int = 0.0
+ attention_probs_dropout_prob: float | int = 0.0
+ initializer_range: float = 0.02
+ layer_norm_eps: float = 1e-12
+ image_size: int | list[int] | tuple[int, int] = 224
+ patch_size: int | list[int] | tuple[int, int] = 16
+ num_channels: int = 3
+ qkv_bias: bool = True
+ pooler_output_size: int | None = None
+ pooler_act: str = "tanh"
+
+ def __post_init__(self, **kwargs):
+ self.pooler_output_size = self.pooler_output_size if self.pooler_output_size else self.hidden_size
+ super().__post_init__(**kwargs)
+
+
+__all__ = ["IJepaConfig"]
diff --git a/third_party/transformers/src/transformers/models/ijepa/convert_ijepa_to_hf.py b/third_party/transformers/src/transformers/models/ijepa/convert_ijepa_to_hf.py
new file mode 100644
index 0000000000000000000000000000000000000000..60c32ab69083f74e059a0f88b1b28314f34f9c9a
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/ijepa/convert_ijepa_to_hf.py
@@ -0,0 +1,265 @@
+# Copyright 2024 The HuggingFace Inc. team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Convert IJEPA checkpoints from the original repository.
+
+URL: https://github.com/facebookresearch/ijepa
+"""
+
+import argparse
+import gc
+import re
+from io import BytesIO
+from pathlib import Path
+
+import httpx
+import torch
+from PIL import Image
+
+from transformers import (
+ IJepaConfig,
+ IJepaModel,
+ ViTImageProcessor,
+)
+from transformers.utils import logging
+
+
+logging.set_verbosity_info()
+logger = logging.get_logger(__name__)
+
+# fmt: off
+ORIGINAL_TO_CONVERTED_KEY_MAPPING = {
+ # Projection layer + position embeddings
+ r"pos_embed": r"embeddings.position_embeddings",
+ r"patch_embed.proj.weight": r"embeddings.patch_embeddings.projection.weight",
+ r"patch_embed.proj.bias": r"embeddings.patch_embeddings.projection.bias",
+
+ # Encoder layers: Layernorms, Attention, Feedforward layers
+ r"blocks.(\d+).norm1.weight": r"encoder.layer.\1.layernorm_before.weight",
+ r"blocks.(\d+).norm1.bias": r"encoder.layer.\1.layernorm_before.bias",
+ r"blocks.(\d+).attn.proj.weight": r"encoder.layer.\1.attention.output.dense.weight",
+ r"blocks.(\d+).attn.proj.bias": r"encoder.layer.\1.attention.output.dense.bias",
+ r"blocks.(\d+).norm2.weight": r"encoder.layer.\1.layernorm_after.weight",
+ r"blocks.(\d+).norm2.bias": r"encoder.layer.\1.layernorm_after.bias",
+ r"blocks.(\d+).mlp.fc1.weight": r"encoder.layer.\1.intermediate.dense.weight",
+ r"blocks.(\d+).mlp.fc1.bias": r"encoder.layer.\1.intermediate.dense.bias",
+ r"blocks.(\d+).mlp.fc2.weight": r"encoder.layer.\1.output.dense.weight",
+ r"blocks.(\d+).mlp.fc2.bias": r"encoder.layer.\1.output.dense.bias",
+
+ # Layernorm + pooler
+ r"norm.weight": r"layernorm.weight",
+ r"norm.bias": r"layernorm.bias",
+}
+# fmt: on
+
+
+def convert_old_keys_to_new_keys(state_dict_keys: dict | None = None):
+ """
+ Converts old keys to new keys using the mapping and dynamically removes the 'ijepa.' prefix if necessary.
+
+ Args:
+ state_dict_keys (dict): The keys from the state_dict to convert.
+
+ Returns:
+ dict: A mapping from old keys to new keys.
+ """
+ output_dict = {}
+ if state_dict_keys is not None:
+ old_text = "\n".join(state_dict_keys)
+ new_text = old_text
+
+ # Apply regex-based mapping
+ for pattern, replacement in ORIGINAL_TO_CONVERTED_KEY_MAPPING.items():
+ if replacement is None:
+ new_text = re.sub(pattern, "", new_text) # Skip the key
+ continue
+ new_text = re.sub(pattern, replacement, new_text)
+
+ output_dict = dict(zip(old_text.split("\n"), new_text.split("\n")))
+
+ return output_dict
+
+
+# we split up the matrix of each encoder layer into queries, keys and values
+def read_in_q_k_v(state_dict, config):
+ for i in range(config.num_hidden_layers):
+ # read in weights + bias of input projection layer (in timm, this is a single matrix + bias)
+ in_proj_weight = state_dict.pop(f"blocks.{i}.attn.qkv.weight")
+ in_proj_bias = state_dict.pop(f"blocks.{i}.attn.qkv.bias")
+ # next, add query, keys and values (in that order) to the state dict
+ state_dict[f"encoder.layer.{i}.attention.attention.query.weight"] = in_proj_weight[: config.hidden_size, :]
+ state_dict[f"encoder.layer.{i}.attention.attention.query.bias"] = in_proj_bias[: config.hidden_size]
+ state_dict[f"encoder.layer.{i}.attention.attention.key.weight"] = in_proj_weight[
+ config.hidden_size : config.hidden_size * 2, :
+ ]
+ state_dict[f"encoder.layer.{i}.attention.attention.key.bias"] = in_proj_bias[
+ config.hidden_size : config.hidden_size * 2
+ ]
+ state_dict[f"encoder.layer.{i}.attention.attention.value.weight"] = in_proj_weight[-config.hidden_size :, :]
+ state_dict[f"encoder.layer.{i}.attention.attention.value.bias"] = in_proj_bias[-config.hidden_size :]
+
+
+def rename_key(dct, old, new):
+ val = dct.pop(old)
+ dct[new] = val
+
+
+# We will verify our results on an image of cute cats
+def prepare_img():
+ url = "http://images.cocodataset.org/val2017/000000039769.jpg"
+ with httpx.stream("GET", url) as response:
+ image = Image.open(BytesIO(response.read()))
+ return image
+
+
+def get_ijepa_config(model_name):
+ patch_size = int(model_name.split("_")[1][4:])
+ config = IJepaConfig(patch_size=patch_size)
+ if "vith" in model_name:
+ config.hidden_size = 1280
+ config.num_hidden_layers = 32
+ config.num_attention_heads = 16
+ config.layer_norm_eps = 1e-6
+ config.mlp_ratio = 4
+ config.intermediate_size = 5120
+ if model_name == "ijepa_vith16_1k":
+ config.image_size = 448
+ elif "vitg" in model_name:
+ config.hidden_size = 1408
+ config.num_hidden_layers = 40
+ config.num_attention_heads = 16
+ config.layer_norm_eps = 1e-6
+ config.mlp_ratio = 48 / 11
+ config.intermediate_size = 6144
+ else:
+ raise ValueError("Model not supported, only supports huge and giant models.")
+ return config
+
+
+@torch.no_grad()
+def write_model(model_name, output_dir, push_to_hub, verify_logits):
+ """
+ Copy/paste/tweak model's weights to our IJEPA structure.
+ """
+
+ # define default IJEPA configuration
+ config = get_ijepa_config(model_name)
+
+ checkpoint_mapping = {
+ "ijepa_vith14_1k": "https://dl.fbaipublicfiles.com/ijepa/IN1K-vit.h.14-300e.pth.tar",
+ "ijepa_vith14_22k": "https://dl.fbaipublicfiles.com/ijepa/IN22K-vit.h.14-900e.pth.tar",
+ "ijepa_vith16_1k": "https://dl.fbaipublicfiles.com/ijepa/IN1K-vit.h.16-448px-300e.pth.tar",
+ "ijepa_vitg16_22k": "https://dl.fbaipublicfiles.com/ijepa/IN22K-vit.g.16-600e.pth.tar",
+ }
+
+ # Load original checkpoint
+ checkpoint_url = checkpoint_mapping[model_name]
+ original_state_dict = torch.hub.load_state_dict_from_url(checkpoint_url, map_location="cpu")["encoder"]
+ original_state_dict = {k.replace("module.", ""): v for k, v in original_state_dict.items()}
+
+ # Rename keys
+ state_dict = original_state_dict.copy()
+ new_keys = convert_old_keys_to_new_keys(state_dict.keys())
+ for old_key, new_key in new_keys.items():
+ rename_key(state_dict, old_key, new_key)
+ read_in_q_k_v(state_dict, config)
+
+ # load HuggingFace model
+ model = IJepaModel(config, add_pooling_layer=False).eval()
+ model.load_state_dict(state_dict)
+ size = {"height": config.image_size, "width": config.image_size}
+ image_processor = ViTImageProcessor(size=size)
+
+ if verify_logits:
+ # Check outputs on an image, prepared by ViTImageProcessor
+ encoding = image_processor(images=prepare_img(), return_tensors="pt")
+ pixel_values = encoding["pixel_values"]
+ with torch.no_grad():
+ outputs = model(pixel_values)
+
+ expected_slices = {
+ "ijepa_vith14_1k": torch.Tensor(
+ [[-0.0621, -0.0054, -2.7513], [-0.1952, 0.0909, -3.9536], [0.0942, -0.0331, -1.2833]]
+ ),
+ "ijepa_vith14_22k": torch.Tensor(
+ [[0.0358, -0.0045, -0.2154], [0.0418, -0.0246, 0.0108], [0.2529, -0.0345, -0.0246]]
+ ),
+ "ijepa_vith16_1k": torch.Tensor(
+ [[0.5145, -0.1259, 0.0615], [0.1132, 0.0028, -0.0496], [1.1586, -0.0056, -0.0387]]
+ ),
+ "ijepa_vitg16_22k": torch.Tensor(
+ [[0.0512, -0.0510, -0.0649], [0.1972, 0.0380, -0.0790], [0.1667, -0.0834, -0.1240]]
+ ),
+ }
+
+ assert torch.allclose(
+ expected_slices[model_name],
+ outputs.last_hidden_state[0, :3, :3],
+ atol=1e-4,
+ )
+
+ if output_dir:
+ Path(output_dir).mkdir(exist_ok=True)
+ print(f"Saving model {model_name} to {output_dir}")
+ image_processor.save_pretrained(output_dir)
+ model.save_pretrained(output_dir)
+
+ if push_to_hub:
+ image_processor.push_to_hub(repo_id=f"jmtzt/{model_name}")
+ model.push_to_hub(repo_id=f"jmtzt/{model_name}")
+
+ if output_dir:
+ del model, state_dict
+ gc.collect()
+ print("Reloading the model to check if it's saved correctly.")
+ IJepaModel.from_pretrained(output_dir, device_map="auto")
+ print("Model reloaded successfully.")
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ # Required parameters
+ parser.add_argument(
+ "--model_name",
+ default="ijepa_vith14_1k",
+ type=str,
+ choices=[
+ "ijepa_vith14_1k",
+ "ijepa_vith14_22k",
+ "ijepa_vith16_1k",
+ "ijepa_vitg16_22k",
+ ],
+ help="Name of the model you'd like to convert.",
+ )
+ parser.add_argument(
+ "--output_dir",
+ default=None,
+ type=str,
+ help="Path to the output PyTorch model directory.",
+ )
+ parser.add_argument(
+ "--push_to_hub",
+ action="store_true",
+ help="Whether or not to push the model to the Hugging Face Hub.",
+ )
+ parser.add_argument(
+ "--verify_logits", action="store_false", help="Whether or not to verify logits after conversion."
+ )
+
+ parser.set_defaults()
+ args = parser.parse_args()
+ write_model(args.model_name, args.output_dir, args.push_to_hub, args.verify_logits)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/third_party/transformers/src/transformers/models/ijepa/modeling_ijepa.py b/third_party/transformers/src/transformers/models/ijepa/modeling_ijepa.py
new file mode 100644
index 0000000000000000000000000000000000000000..bd1d9119a982347b3178df7bfb7d9643f7d9b74e
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/ijepa/modeling_ijepa.py
@@ -0,0 +1,511 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/ijepa/modular_ijepa.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_ijepa.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+import collections.abc
+from collections.abc import Callable
+
+import torch
+import torch.nn as nn
+
+from ... import initialization as init
+from ...activations import ACT2FN
+from ...modeling_layers import GradientCheckpointingLayer
+from ...modeling_outputs import BaseModelOutput, BaseModelOutputWithPooling, ImageClassifierOutput
+from ...modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
+from ...processing_utils import Unpack
+from ...utils import TransformersKwargs, auto_docstring, torch_int
+from ...utils.generic import can_return_tuple, merge_with_config_defaults
+from ...utils.output_capturing import capture_outputs
+from .configuration_ijepa import IJepaConfig
+
+
+class IJepaPatchEmbeddings(nn.Module):
+ """
+ This class turns `pixel_values` of shape `(batch_size, num_channels, height, width)` into the initial
+ `hidden_states` (patch embeddings) of shape `(batch_size, seq_length, hidden_size)` to be consumed by a
+ Transformer.
+ """
+
+ def __init__(self, config: IJepaConfig):
+ super().__init__()
+ image_size, patch_size = config.image_size, config.patch_size
+ num_channels, hidden_size = config.num_channels, config.hidden_size
+
+ image_size = image_size if isinstance(image_size, collections.abc.Iterable) else (image_size, image_size)
+ patch_size = patch_size if isinstance(patch_size, collections.abc.Iterable) else (patch_size, patch_size)
+ num_patches = (image_size[1] // patch_size[1]) * (image_size[0] // patch_size[0])
+ self.image_size = image_size
+ self.patch_size = patch_size
+ self.num_channels = num_channels
+ self.num_patches = num_patches
+
+ self.projection = nn.Conv2d(num_channels, hidden_size, kernel_size=patch_size, stride=patch_size)
+
+ def forward(self, pixel_values: torch.Tensor, interpolate_pos_encoding: bool = False) -> torch.Tensor:
+ batch_size, num_channels, height, width = pixel_values.shape
+ if num_channels != self.num_channels:
+ raise ValueError(
+ "Make sure that the channel dimension of the pixel values match with the one set in the configuration."
+ f" Expected {self.num_channels} but got {num_channels}."
+ )
+ if not interpolate_pos_encoding:
+ if height != self.image_size[0] or width != self.image_size[1]:
+ raise ValueError(
+ f"Input image size ({height}*{width}) doesn't match model"
+ f" ({self.image_size[0]}*{self.image_size[1]})."
+ )
+ embeddings = self.projection(pixel_values).flatten(2).transpose(1, 2)
+ return embeddings
+
+
+class IJepaEmbeddings(nn.Module):
+ """
+ Construct the CLS token, position and patch embeddings. Optionally, also the mask token.
+ """
+
+ def __init__(self, config: IJepaConfig, use_mask_token: bool = False) -> None:
+ super().__init__()
+ self.mask_token = nn.Parameter(torch.zeros(1, 1, config.hidden_size)) if use_mask_token else None
+ self.patch_embeddings = IJepaPatchEmbeddings(config)
+ num_patches = self.patch_embeddings.num_patches
+ self.position_embeddings = nn.Parameter(torch.randn(1, num_patches, config.hidden_size))
+ self.dropout = nn.Dropout(config.hidden_dropout_prob)
+ self.patch_size = config.patch_size
+ self.config = config
+
+ def interpolate_pos_encoding(self, embeddings: torch.Tensor, height: int, width: int) -> torch.Tensor:
+ """
+ This method allows to interpolate the pre-trained position encodings, to be able to use the model on higher resolution
+ images. This method is also adapted to support torch.jit tracing.
+
+ Adapted from:
+ - https://github.com/facebookresearch/dino/blob/de9ee3df6cf39fac952ab558447af1fa1365362a/vision_transformer.py#L174-L194, and
+ - https://github.com/facebookresearch/dinov2/blob/e1277af2ba9496fbadf7aec6eba56e8d882d1e35/dinov2/models/vision_transformer.py#L179-L211
+ """
+
+ num_patches = embeddings.shape[1]
+ num_positions = self.position_embeddings.shape[1]
+
+ # always interpolate when tracing to ensure the exported model works for dynamic input shapes
+ if not torch.jit.is_tracing() and num_patches == num_positions and height == width:
+ return self.position_embeddings
+
+ patch_pos_embed = self.position_embeddings
+
+ dim = embeddings.shape[-1]
+
+ new_height = height // self.patch_size
+ new_width = width // self.patch_size
+
+ sqrt_num_positions = torch_int(num_positions**0.5)
+ patch_pos_embed = patch_pos_embed.reshape(1, sqrt_num_positions, sqrt_num_positions, dim)
+ patch_pos_embed = patch_pos_embed.permute(0, 3, 1, 2)
+
+ patch_pos_embed = nn.functional.interpolate(
+ patch_pos_embed,
+ size=(new_height, new_width),
+ mode="bicubic",
+ align_corners=False,
+ )
+
+ patch_pos_embed = patch_pos_embed.permute(0, 2, 3, 1).view(1, -1, dim)
+
+ return patch_pos_embed
+
+ def forward(
+ self,
+ pixel_values: torch.Tensor,
+ bool_masked_pos: torch.BoolTensor | None = None,
+ interpolate_pos_encoding: bool = False,
+ ) -> torch.Tensor:
+ batch_size, _, height, width = pixel_values.shape
+ embeddings = self.patch_embeddings(pixel_values, interpolate_pos_encoding=interpolate_pos_encoding)
+
+ if bool_masked_pos is not None:
+ seq_length = embeddings.shape[1]
+ mask_tokens = self.mask_token.expand(batch_size, seq_length, -1)
+ # replace the masked visual tokens by mask_tokens
+ mask = bool_masked_pos.unsqueeze(-1).type_as(mask_tokens)
+ embeddings = embeddings * (1.0 - mask) + mask_tokens * mask
+
+ # add positional encoding to each token
+ if interpolate_pos_encoding:
+ embeddings = embeddings + self.interpolate_pos_encoding(embeddings, height, width)
+ else:
+ embeddings = embeddings + self.position_embeddings
+
+ embeddings = self.dropout(embeddings)
+
+ return embeddings
+
+
+def eager_attention_forward(
+ module: nn.Module,
+ query: torch.Tensor,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ attention_mask: torch.Tensor | None,
+ scaling: float | None = None,
+ dropout: float = 0.0,
+ **kwargs: Unpack[TransformersKwargs],
+):
+ if scaling is None:
+ scaling = query.size(-1) ** -0.5
+
+ # Take the dot product between "query" and "key" to get the raw attention scores.
+ attn_weights = torch.matmul(query, key.transpose(2, 3)) * scaling
+
+ if attention_mask is not None:
+ attn_weights = attn_weights + attention_mask
+
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1)
+ attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
+
+ attn_output = torch.matmul(attn_weights, value)
+ attn_output = attn_output.transpose(1, 2).contiguous()
+
+ return attn_output, attn_weights
+
+
+class IJepaSelfAttention(nn.Module):
+ def __init__(self, config: IJepaConfig):
+ super().__init__()
+ if config.hidden_size % config.num_attention_heads != 0 and not hasattr(config, "embedding_size"):
+ raise ValueError(
+ f"The hidden size {config.hidden_size} is not a multiple of the number of attention "
+ f"heads {config.num_attention_heads}."
+ )
+
+ self.config = config
+ self.num_attention_heads = config.num_attention_heads
+ self.attention_head_size = int(config.hidden_size / config.num_attention_heads)
+ self.all_head_size = self.num_attention_heads * self.attention_head_size
+ self.dropout_prob = config.attention_probs_dropout_prob
+ self.scaling = self.attention_head_size**-0.5
+ self.is_causal = False
+
+ self.query = nn.Linear(config.hidden_size, self.all_head_size, bias=config.qkv_bias)
+ self.key = nn.Linear(config.hidden_size, self.all_head_size, bias=config.qkv_bias)
+ self.value = nn.Linear(config.hidden_size, self.all_head_size, bias=config.qkv_bias)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple[torch.Tensor, torch.Tensor]:
+ batch_size = hidden_states.shape[0]
+ new_shape = batch_size, -1, self.num_attention_heads, self.attention_head_size
+
+ key_layer = self.key(hidden_states).view(*new_shape).transpose(1, 2)
+ value_layer = self.value(hidden_states).view(*new_shape).transpose(1, 2)
+ query_layer = self.query(hidden_states).view(*new_shape).transpose(1, 2)
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ context_layer, attention_probs = attention_interface(
+ self,
+ query_layer,
+ key_layer,
+ value_layer,
+ None,
+ is_causal=self.is_causal,
+ scaling=self.scaling,
+ dropout=0.0 if not self.training else self.dropout_prob,
+ **kwargs,
+ )
+
+ new_context_layer_shape = context_layer.size()[:-2] + (self.all_head_size,)
+ context_layer = context_layer.reshape(new_context_layer_shape)
+
+ return context_layer, attention_probs
+
+
+class IJepaSelfOutput(nn.Module):
+ """
+ The residual connection is defined in IJepaLayer instead of here (as is the case with other models), due to the
+ layernorm applied before each block.
+ """
+
+ def __init__(self, config: IJepaConfig):
+ super().__init__()
+ self.dense = nn.Linear(config.hidden_size, config.hidden_size)
+ self.dropout = nn.Dropout(config.hidden_dropout_prob)
+
+ def forward(self, hidden_states: torch.Tensor, input_tensor: torch.Tensor) -> torch.Tensor:
+ hidden_states = self.dense(hidden_states)
+ hidden_states = self.dropout(hidden_states)
+ return hidden_states
+
+
+class IJepaAttention(nn.Module):
+ def __init__(self, config: IJepaConfig):
+ super().__init__()
+ self.attention = IJepaSelfAttention(config)
+ self.output = IJepaSelfOutput(config)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> torch.Tensor:
+ self_attn_output, _ = self.attention(hidden_states, **kwargs)
+ output = self.output(self_attn_output, hidden_states)
+ return output
+
+
+class IJepaIntermediate(nn.Module):
+ def __init__(self, config: IJepaConfig):
+ super().__init__()
+ self.dense = nn.Linear(config.hidden_size, config.intermediate_size)
+ if isinstance(config.hidden_act, str):
+ self.intermediate_act_fn = ACT2FN[config.hidden_act]
+ else:
+ self.intermediate_act_fn = config.hidden_act
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ hidden_states = self.dense(hidden_states)
+ hidden_states = self.intermediate_act_fn(hidden_states)
+ return hidden_states
+
+
+class IJepaOutput(nn.Module):
+ def __init__(self, config: IJepaConfig):
+ super().__init__()
+ self.dense = nn.Linear(config.intermediate_size, config.hidden_size)
+ self.dropout = nn.Dropout(config.hidden_dropout_prob)
+
+ def forward(self, hidden_states: torch.Tensor, input_tensor: torch.Tensor) -> torch.Tensor:
+ hidden_states = self.dense(hidden_states)
+ hidden_states = self.dropout(hidden_states)
+ hidden_states = hidden_states + input_tensor
+ return hidden_states
+
+
+class IJepaLayer(GradientCheckpointingLayer):
+ """This corresponds to the Block class in the timm implementation."""
+
+ def __init__(self, config: IJepaConfig):
+ super().__init__()
+ self.chunk_size_feed_forward = config.chunk_size_feed_forward
+ self.seq_len_dim = 1
+ self.attention = IJepaAttention(config)
+ self.intermediate = IJepaIntermediate(config)
+ self.output = IJepaOutput(config)
+ self.layernorm_before = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
+ self.layernorm_after = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> torch.Tensor:
+ hidden_states_norm = self.layernorm_before(hidden_states)
+ attention_output = self.attention(hidden_states_norm, **kwargs)
+
+ # first residual connection
+ hidden_states = attention_output + hidden_states
+
+ # in IJepa, layernorm is also applied after self-attention
+ layer_output = self.layernorm_after(hidden_states)
+ layer_output = self.intermediate(layer_output)
+
+ # second residual connection is done here
+ layer_output = self.output(layer_output, hidden_states)
+
+ return layer_output
+
+
+@auto_docstring
+class IJepaPreTrainedModel(PreTrainedModel):
+ config: IJepaConfig
+ base_model_prefix = "ijepa"
+ main_input_name = "pixel_values"
+ input_modalities = ("image",)
+ supports_gradient_checkpointing = True
+ _no_split_modules = ["IJepaEmbeddings", "IJepaLayer"]
+ _supports_sdpa = True
+ _supports_flash_attn = True
+ _supports_flex_attn = True
+ _supports_attention_backend = True
+ _can_record_outputs = {
+ "hidden_states": IJepaLayer,
+ "attentions": IJepaSelfAttention,
+ }
+
+ @torch.no_grad()
+ def _init_weights(self, module: nn.Linear | nn.Conv2d | nn.LayerNorm) -> None:
+ """Initialize the weights"""
+ if isinstance(module, (nn.Linear, nn.Conv2d)):
+ init.trunc_normal_(module.weight, mean=0.0, std=self.config.initializer_range)
+ if module.bias is not None:
+ init.zeros_(module.bias)
+ elif isinstance(module, nn.LayerNorm):
+ init.zeros_(module.bias)
+ init.ones_(module.weight)
+ elif isinstance(module, IJepaEmbeddings):
+ init.trunc_normal_(module.position_embeddings, mean=0.0, std=self.config.initializer_range)
+ if module.mask_token is not None:
+ init.zeros_(module.mask_token)
+
+
+class IJepaEncoder(nn.Module):
+ def __init__(self, config: IJepaConfig):
+ super().__init__()
+ self.config = config
+ self.layer = nn.ModuleList([IJepaLayer(config) for _ in range(config.num_hidden_layers)])
+ self.gradient_checkpointing = False
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> BaseModelOutput:
+ for layer_module in self.layer:
+ hidden_states = layer_module(hidden_states, **kwargs)
+
+ return BaseModelOutput(last_hidden_state=hidden_states)
+
+
+class IJepaPooler(nn.Module):
+ def __init__(self, config: IJepaConfig):
+ super().__init__()
+ self.dense = nn.Linear(config.hidden_size, config.pooler_output_size)
+ self.activation = ACT2FN[config.pooler_act]
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ # We "pool" the model by simply taking the hidden state corresponding
+ # to the first token.
+ first_token_tensor = hidden_states[:, 0]
+ pooled_output = self.dense(first_token_tensor)
+ pooled_output = self.activation(pooled_output)
+ return pooled_output
+
+
+@auto_docstring
+class IJepaModel(IJepaPreTrainedModel):
+ def __init__(self, config: IJepaConfig, add_pooling_layer: bool = False, use_mask_token: bool = False):
+ r"""
+ add_pooling_layer (bool, *optional*, defaults to `True`):
+ Whether to add a pooling layer
+ use_mask_token (`bool`, *optional*, defaults to `False`):
+ Whether to use a mask token for masked image modeling.
+ """
+ super().__init__(config)
+ self.config = config
+ self.embeddings = IJepaEmbeddings(config, use_mask_token=use_mask_token)
+ self.encoder = IJepaEncoder(config)
+
+ self.layernorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
+ self.pooler = IJepaPooler(config) if add_pooling_layer else None
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ def get_input_embeddings(self) -> IJepaPatchEmbeddings:
+ return self.embeddings.patch_embeddings
+
+ @merge_with_config_defaults
+ @capture_outputs(tie_last_hidden_states=False)
+ @auto_docstring
+ def forward(
+ self,
+ pixel_values: torch.Tensor | None = None,
+ bool_masked_pos: torch.BoolTensor | None = None,
+ interpolate_pos_encoding: bool | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> BaseModelOutputWithPooling:
+ r"""
+ bool_masked_pos (`torch.BoolTensor` of shape `(batch_size, num_patches)`, *optional*):
+ Boolean masked positions. Indicates which patches are masked (1) and which aren't (0).
+ """
+
+ if pixel_values is None:
+ raise ValueError("You have to specify pixel_values")
+
+ # TODO: maybe have a cleaner way to cast the input (from `ImageProcessor` side?)
+ expected_dtype = self.embeddings.patch_embeddings.projection.weight.dtype
+ if pixel_values.dtype != expected_dtype:
+ pixel_values = pixel_values.to(expected_dtype)
+
+ embedding_output = self.embeddings(
+ pixel_values, bool_masked_pos=bool_masked_pos, interpolate_pos_encoding=interpolate_pos_encoding
+ )
+
+ encoder_outputs: BaseModelOutput = self.encoder(embedding_output)
+
+ sequence_output = encoder_outputs.last_hidden_state
+ sequence_output = self.layernorm(sequence_output)
+ pooled_output = self.pooler(sequence_output) if self.pooler is not None else None
+
+ return BaseModelOutputWithPooling(last_hidden_state=sequence_output, pooler_output=pooled_output)
+
+
+@auto_docstring(
+ custom_intro="""
+ IJepa Model transformer with an image classification head on top (a linear layer on top of the final hidden states)
+ e.g. for ImageNet.
+
+
+
+ Note that it's possible to fine-tune IJepa on higher resolution images than the ones it has been trained on, by
+ setting `interpolate_pos_encoding` to `True` in the forward of the model. This will interpolate the pre-trained
+ position embeddings to the higher resolution.
+
+
+ """
+)
+class IJepaForImageClassification(IJepaPreTrainedModel):
+ def __init__(self, config: IJepaConfig):
+ super().__init__(config)
+
+ self.num_labels = config.num_labels
+ self.ijepa = IJepaModel(config, add_pooling_layer=False)
+
+ # Classifier head
+ self.classifier = nn.Linear(config.hidden_size, config.num_labels) if config.num_labels > 0 else nn.Identity()
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ pixel_values: torch.Tensor | None = None,
+ labels: torch.Tensor | None = None,
+ interpolate_pos_encoding: bool | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> ImageClassifierOutput:
+ r"""
+ labels (`torch.LongTensor` of shape `(batch_size,)`, *optional*):
+ Labels for computing the image classification/regression loss. Indices should be in `[0, ...,
+ config.num_labels - 1]`. If `config.num_labels == 1` a regression loss is computed (Mean-Square loss), If
+ `config.num_labels > 1` a classification loss is computed (Cross-Entropy).
+ """
+
+ outputs: BaseModelOutputWithPooling = self.ijepa(
+ pixel_values,
+ interpolate_pos_encoding=interpolate_pos_encoding,
+ **kwargs,
+ )
+ sequence_output = outputs.last_hidden_state
+ logits = self.classifier(sequence_output.mean(dim=1))
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(labels, logits, self.config, **kwargs)
+
+ return ImageClassifierOutput(
+ loss=loss,
+ logits=logits,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ )
+
+
+__all__ = ["IJepaPreTrainedModel", "IJepaModel", "IJepaForImageClassification"]
diff --git a/third_party/transformers/src/transformers/models/ijepa/modular_ijepa.py b/third_party/transformers/src/transformers/models/ijepa/modular_ijepa.py
new file mode 100644
index 0000000000000000000000000000000000000000..ed9ec63494dd35057dee214652f040258ebeaff9
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/ijepa/modular_ijepa.py
@@ -0,0 +1,176 @@
+import torch
+import torch.nn as nn
+
+from transformers.models.ijepa.configuration_ijepa import IJepaConfig
+
+from ... import initialization as init
+from ...modeling_outputs import BaseModelOutputWithPooling, ImageClassifierOutput
+from ...processing_utils import Unpack
+from ...utils import TransformersKwargs, auto_docstring, torch_int
+from ..vit.modeling_vit import ViTEmbeddings, ViTForImageClassification, ViTModel, ViTPreTrainedModel
+
+
+class IJepaEmbeddings(ViTEmbeddings):
+ def __init__(self, config: IJepaConfig, use_mask_token: bool = False) -> None:
+ super().__init__(config, use_mask_token)
+ # Remove cls_token from IJepaEmbeddings, as it is not used in the model
+ del self.cls_token
+ num_patches = self.patch_embeddings.num_patches
+ self.position_embeddings = nn.Parameter(torch.randn(1, num_patches, config.hidden_size))
+
+ def interpolate_pos_encoding(self, embeddings: torch.Tensor, height: int, width: int) -> torch.Tensor:
+ """
+ This method allows to interpolate the pre-trained position encodings, to be able to use the model on higher resolution
+ images. This method is also adapted to support torch.jit tracing.
+
+ Adapted from:
+ - https://github.com/facebookresearch/dino/blob/de9ee3df6cf39fac952ab558447af1fa1365362a/vision_transformer.py#L174-L194, and
+ - https://github.com/facebookresearch/dinov2/blob/e1277af2ba9496fbadf7aec6eba56e8d882d1e35/dinov2/models/vision_transformer.py#L179-L211
+ """
+
+ num_patches = embeddings.shape[1]
+ num_positions = self.position_embeddings.shape[1]
+
+ # always interpolate when tracing to ensure the exported model works for dynamic input shapes
+ if not torch.jit.is_tracing() and num_patches == num_positions and height == width:
+ return self.position_embeddings
+
+ patch_pos_embed = self.position_embeddings
+
+ dim = embeddings.shape[-1]
+
+ new_height = height // self.patch_size
+ new_width = width // self.patch_size
+
+ sqrt_num_positions = torch_int(num_positions**0.5)
+ patch_pos_embed = patch_pos_embed.reshape(1, sqrt_num_positions, sqrt_num_positions, dim)
+ patch_pos_embed = patch_pos_embed.permute(0, 3, 1, 2)
+
+ patch_pos_embed = nn.functional.interpolate(
+ patch_pos_embed,
+ size=(new_height, new_width),
+ mode="bicubic",
+ align_corners=False,
+ )
+
+ patch_pos_embed = patch_pos_embed.permute(0, 2, 3, 1).view(1, -1, dim)
+
+ return patch_pos_embed
+
+ def forward(
+ self,
+ pixel_values: torch.Tensor,
+ bool_masked_pos: torch.BoolTensor | None = None,
+ interpolate_pos_encoding: bool = False,
+ ) -> torch.Tensor:
+ batch_size, _, height, width = pixel_values.shape
+ embeddings = self.patch_embeddings(pixel_values, interpolate_pos_encoding=interpolate_pos_encoding)
+
+ if bool_masked_pos is not None:
+ seq_length = embeddings.shape[1]
+ mask_tokens = self.mask_token.expand(batch_size, seq_length, -1)
+ # replace the masked visual tokens by mask_tokens
+ mask = bool_masked_pos.unsqueeze(-1).type_as(mask_tokens)
+ embeddings = embeddings * (1.0 - mask) + mask_tokens * mask
+
+ # add positional encoding to each token
+ if interpolate_pos_encoding:
+ embeddings = embeddings + self.interpolate_pos_encoding(embeddings, height, width)
+ else:
+ embeddings = embeddings + self.position_embeddings
+
+ embeddings = self.dropout(embeddings)
+
+ return embeddings
+
+
+@auto_docstring
+class IJepaPreTrainedModel(ViTPreTrainedModel):
+ @torch.no_grad()
+ def _init_weights(self, module: nn.Linear | nn.Conv2d | nn.LayerNorm) -> None:
+ """Initialize the weights"""
+ if isinstance(module, (nn.Linear, nn.Conv2d)):
+ init.trunc_normal_(module.weight, mean=0.0, std=self.config.initializer_range)
+ if module.bias is not None:
+ init.zeros_(module.bias)
+ elif isinstance(module, nn.LayerNorm):
+ init.zeros_(module.bias)
+ init.ones_(module.weight)
+ elif isinstance(module, IJepaEmbeddings):
+ init.trunc_normal_(module.position_embeddings, mean=0.0, std=self.config.initializer_range)
+ if module.mask_token is not None:
+ init.zeros_(module.mask_token)
+
+
+class IJepaModel(IJepaPreTrainedModel, ViTModel):
+ def __init__(self, config: IJepaConfig, add_pooling_layer: bool = False, use_mask_token: bool = False):
+ r"""
+ add_pooling_layer (bool, *optional*, defaults to `True`):
+ Whether to add a pooling layer
+ use_mask_token (`bool`, *optional*, defaults to `False`):
+ Whether to use a mask token for masked image modeling.
+ """
+ super().__init__(config)
+ self.config = config
+ self.embeddings = IJepaEmbeddings(config, use_mask_token=use_mask_token)
+
+
+@auto_docstring(
+ custom_intro="""
+ IJepa Model transformer with an image classification head on top (a linear layer on top of the final hidden states)
+ e.g. for ImageNet.
+
+
+
+ Note that it's possible to fine-tune IJepa on higher resolution images than the ones it has been trained on, by
+ setting `interpolate_pos_encoding` to `True` in the forward of the model. This will interpolate the pre-trained
+ position embeddings to the higher resolution.
+
+
+ """
+)
+class IJepaForImageClassification(IJepaPreTrainedModel, ViTForImageClassification):
+ def __init__(self, config: IJepaConfig):
+ super().__init__(config)
+ self.ijepa = IJepaModel(config, add_pooling_layer=False)
+ self.post_init()
+
+ def forward(
+ self,
+ pixel_values: torch.Tensor | None = None,
+ labels: torch.Tensor | None = None,
+ interpolate_pos_encoding: bool | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> ImageClassifierOutput:
+ r"""
+ labels (`torch.LongTensor` of shape `(batch_size,)`, *optional*):
+ Labels for computing the image classification/regression loss. Indices should be in `[0, ...,
+ config.num_labels - 1]`. If `config.num_labels == 1` a regression loss is computed (Mean-Square loss), If
+ `config.num_labels > 1` a classification loss is computed (Cross-Entropy).
+ """
+
+ outputs: BaseModelOutputWithPooling = self.ijepa(
+ pixel_values,
+ interpolate_pos_encoding=interpolate_pos_encoding,
+ **kwargs,
+ )
+ sequence_output = outputs.last_hidden_state
+ logits = self.classifier(sequence_output.mean(dim=1))
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(labels, logits, self.config, **kwargs)
+
+ return ImageClassifierOutput(
+ loss=loss,
+ logits=logits,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ )
+
+
+__all__ = [
+ "IJepaPreTrainedModel",
+ "IJepaModel",
+ "IJepaForImageClassification",
+]
diff --git a/third_party/transformers/src/transformers/models/imagegpt/__init__.py b/third_party/transformers/src/transformers/models/imagegpt/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..2f4acb12aabb2516d6272bb3e06b26a23506bc51
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/imagegpt/__init__.py
@@ -0,0 +1,30 @@
+# Copyright 2024 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_imagegpt import *
+ from .feature_extraction_imagegpt import *
+ from .image_processing_imagegpt import *
+ from .image_processing_pil_imagegpt import *
+ from .modeling_imagegpt import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/imagegpt/configuration_imagegpt.py b/third_party/transformers/src/transformers/models/imagegpt/configuration_imagegpt.py
new file mode 100644
index 0000000000000000000000000000000000000000..7f520482428e5af9287789f6197cef2ce97ccef7
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/imagegpt/configuration_imagegpt.py
@@ -0,0 +1,79 @@
+# Copyright 2021 The HuggingFace Inc. team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""OpenAI ImageGPT configuration"""
+
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...utils import auto_docstring
+
+
+@auto_docstring(checkpoint="openai/imagegpt-small")
+@strict
+class ImageGPTConfig(PreTrainedConfig):
+ r"""
+ scale_attn_by_inverse_layer_idx (`bool`, *optional*, defaults to `False`):
+ Whether to additionally scale attention weights by `1 / layer_idx + 1`.
+ reorder_and_upcast_attn (`bool`, *optional*, defaults to `False`):
+ Whether to scale keys (K) prior to computing attention (dot-product) and upcast attention
+ dot-product/softmax to float() when training with mixed precision.
+
+ Example:
+
+ ```python
+ >>> from transformers import ImageGPTConfig, ImageGPTModel
+
+ >>> # Initializing a ImageGPT configuration
+ >>> configuration = ImageGPTConfig()
+
+ >>> # Initializing a model (with random weights) from the configuration
+ >>> model = ImageGPTModel(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "imagegpt"
+ keys_to_ignore_at_inference = ["past_key_values"]
+ attribute_map = {
+ "hidden_size": "n_embd",
+ "max_position_embeddings": "n_positions",
+ "num_attention_heads": "n_head",
+ "num_hidden_layers": "n_layer",
+ }
+
+ vocab_size: int = 512 + 1 # add one for start of sentence (sos) token
+ n_positions: int = 32 * 32
+ n_embd: int = 512
+ n_layer: int = 24
+ n_head: int = 8
+ n_inner: int | None = None
+ activation_function: str = "quick_gelu"
+ resid_pdrop: float | int = 0.1
+ embd_pdrop: float | int = 0.1
+ attn_pdrop: float | int = 0.1
+ layer_norm_epsilon: float = 1e-5
+ initializer_range: float = 0.02
+ scale_attn_weights: bool = True
+ use_cache: bool = True
+ tie_word_embeddings: bool = False
+ scale_attn_by_inverse_layer_idx: bool = False
+ reorder_and_upcast_attn: bool = False
+ add_cross_attention: bool = False
+ pad_token_id: int | None = None
+ bos_token_id: int | None = None
+ eos_token_id: int | list[int] | None = None
+
+
+__all__ = ["ImageGPTConfig"]
diff --git a/third_party/transformers/src/transformers/models/imagegpt/convert_imagegpt_original_tf2_to_pytorch.py b/third_party/transformers/src/transformers/models/imagegpt/convert_imagegpt_original_tf2_to_pytorch.py
new file mode 100644
index 0000000000000000000000000000000000000000..6425a11452c22aceb8f3c365c02509780c663cae
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/imagegpt/convert_imagegpt_original_tf2_to_pytorch.py
@@ -0,0 +1,183 @@
+# Copyright 2021 The HuggingFace Inc. team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Convert OpenAI Image GPT checkpoints."""
+
+import argparse
+import os
+
+import torch
+
+from transformers import ImageGPTConfig, ImageGPTForCausalLM
+from transformers.utils import CONFIG_NAME, WEIGHTS_NAME, logging
+
+
+logging.set_verbosity_info()
+logger = logging.get_logger(__name__)
+
+
+def load_tf_weights_in_imagegpt(model, config, imagegpt_checkpoint_path):
+ """
+ Load tf checkpoints in a pytorch model
+ """
+ try:
+ import re
+
+ import tensorflow as tf
+ except ImportError:
+ logger.error(
+ "Loading a TensorFlow model in PyTorch, requires TensorFlow to be installed. Please see "
+ "https://www.tensorflow.org/install/ for installation instructions."
+ )
+ raise
+ tf_path = os.path.abspath(imagegpt_checkpoint_path)
+ logger.info(f"Converting TensorFlow checkpoint from {tf_path}")
+ # Load weights from TF model
+ init_vars = tf.train.list_variables(tf_path)
+ names = []
+ arrays = []
+
+ for name, shape in init_vars:
+ logger.info(f"Loading TF weight {name} with shape {shape}")
+ array = tf.train.load_variable(tf_path, name)
+ names.append(name)
+ arrays.append(array.squeeze())
+
+ for name, array in zip(names, arrays):
+ name = name[6:] # skip "model/"
+ name = name.split("/")
+
+ # adam_v and adam_m are variables used in AdamWeightDecayOptimizer to calculated m and v
+ # which are not required for using pretrained model
+ if (
+ any(
+ n in ["adam_v", "adam_m", "AdamWeightDecayOptimizer", "AdamWeightDecayOptimizer_1", "global_step"]
+ for n in name
+ )
+ or name[-1] == "_step"
+ ):
+ logger.info(f"Skipping {'/'.join(name)}")
+ continue
+
+ pointer = model
+ if name[-1] != "wtet":
+ pointer = getattr(pointer, "transformer")
+
+ for m_name in name:
+ if re.fullmatch(r"[A-Za-z]+\d+", m_name):
+ scope_names = re.split(r"(\d+)", m_name)
+ else:
+ scope_names = [m_name]
+
+ if scope_names[0] == "w" or scope_names[0] == "g":
+ pointer = getattr(pointer, "weight")
+ elif scope_names[0] == "b":
+ pointer = getattr(pointer, "bias")
+ elif scope_names[0] == "wpe" or scope_names[0] == "wte":
+ pointer = getattr(pointer, scope_names[0])
+ pointer = getattr(pointer, "weight")
+ elif scope_names[0] in ["q_proj", "k_proj", "v_proj"]:
+ pointer = getattr(pointer, "c_attn")
+ pointer = getattr(pointer, "weight")
+ elif len(name) == 3 and name[1] == "attn" and scope_names[0] == "c_proj":
+ pointer = getattr(pointer, scope_names[0])
+ pointer = getattr(pointer, "weight")
+ elif scope_names[0] == "wtet":
+ pointer = getattr(pointer, "lm_head")
+ pointer = getattr(pointer, "weight")
+ elif scope_names[0] == "sos":
+ pointer = getattr(pointer, "wte")
+ pointer = getattr(pointer, "weight")
+ else:
+ pointer = getattr(pointer, scope_names[0])
+ if len(scope_names) >= 2:
+ num = int(scope_names[1])
+ pointer = pointer[num]
+
+ if len(name) > 1 and name[1] == "attn" or name[-1] == "wtet" or name[-1] == "sos" or name[-1] == "wte":
+ pass # array is used to initialize only part of the pointer so sizes won't match
+ else:
+ try:
+ assert pointer.shape == array.shape
+ except AssertionError as e:
+ e.args += (pointer.shape, array.shape)
+ raise
+
+ logger.info(f"Initialize PyTorch weight {name}")
+
+ if name[-1] == "q_proj":
+ pointer.data[:, : config.n_embd] = torch.from_numpy(array.reshape(config.n_embd, config.n_embd)).T
+ elif name[-1] == "k_proj":
+ pointer.data[:, config.n_embd : 2 * config.n_embd] = torch.from_numpy(
+ array.reshape(config.n_embd, config.n_embd)
+ ).T
+ elif name[-1] == "v_proj":
+ pointer.data[:, 2 * config.n_embd :] = torch.from_numpy(array.reshape(config.n_embd, config.n_embd)).T
+ elif len(name) == 3 and name[1] == "attn" and name[2] == "c_proj":
+ pointer.data = torch.from_numpy(array.reshape(config.n_embd, config.n_embd))
+ elif name[-1] == "wtet":
+ pointer.data = torch.from_numpy(array)
+ elif name[-1] == "wte":
+ pointer.data[: config.vocab_size - 1, :] = torch.from_numpy(array)
+ elif name[-1] == "sos":
+ pointer.data[-1] = torch.from_numpy(array)
+ else:
+ pointer.data = torch.from_numpy(array)
+
+ return model
+
+
+def convert_imagegpt_checkpoint_to_pytorch(imagegpt_checkpoint_path, model_size, pytorch_dump_folder_path):
+ # Construct configuration depending on size
+ MODELS = {"small": (512, 8, 24), "medium": (1024, 8, 36), "large": (1536, 16, 48)}
+ n_embd, n_head, n_layer = MODELS[model_size] # set model hyperparameters
+ config = ImageGPTConfig(n_embd=n_embd, n_layer=n_layer, n_head=n_head)
+ model = ImageGPTForCausalLM(config)
+
+ # Load weights from numpy
+ load_tf_weights_in_imagegpt(model, config, imagegpt_checkpoint_path)
+
+ # Save pytorch-model
+ pytorch_weights_dump_path = pytorch_dump_folder_path + "/" + WEIGHTS_NAME
+ pytorch_config_dump_path = pytorch_dump_folder_path + "/" + CONFIG_NAME
+ print(f"Save PyTorch model to {pytorch_weights_dump_path}")
+ torch.save(model.state_dict(), pytorch_weights_dump_path)
+ print(f"Save configuration file to {pytorch_config_dump_path}")
+ with open(pytorch_config_dump_path, "w", encoding="utf-8") as f:
+ f.write(config.to_json_string())
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ # Required parameters
+ parser.add_argument(
+ "--imagegpt_checkpoint_path",
+ default=None,
+ type=str,
+ required=True,
+ help="Path to the TensorFlow checkpoint path.",
+ )
+ parser.add_argument(
+ "--model_size",
+ default=None,
+ type=str,
+ required=True,
+ help="Size of the model (can be either 'small', 'medium' or 'large').",
+ )
+ parser.add_argument(
+ "--pytorch_dump_folder_path", default=None, type=str, required=True, help="Path to the output PyTorch model."
+ )
+ args = parser.parse_args()
+ convert_imagegpt_checkpoint_to_pytorch(
+ args.imagegpt_checkpoint_path, args.model_size, args.pytorch_dump_folder_path
+ )
diff --git a/third_party/transformers/src/transformers/models/imagegpt/image_processing_imagegpt.py b/third_party/transformers/src/transformers/models/imagegpt/image_processing_imagegpt.py
new file mode 100644
index 0000000000000000000000000000000000000000..924ed268aff2e70e474b60b466e1a41954074e89
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/imagegpt/image_processing_imagegpt.py
@@ -0,0 +1,192 @@
+# Copyright 2022 The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Image processor class for ImageGPT."""
+
+from typing import Union
+
+import numpy as np
+import torch
+from torchvision.transforms.v2 import functional as tvF
+
+from ...image_processing_backends import TorchvisionBackend
+from ...image_processing_utils import BatchFeature
+from ...image_transforms import group_images_by_shape, reorder_images
+from ...image_utils import PILImageResampling, SizeDict
+from ...processing_utils import ImagesKwargs, Unpack
+from ...utils import (
+ TensorType,
+ auto_docstring,
+)
+
+
+class ImageGPTImageProcessorKwargs(ImagesKwargs, total=False):
+ r"""
+ clusters (`np.ndarray` or `list[list[int]]` or `torch.Tensor`, *optional*, defaults to `self.clusters`):
+ The color clusters to use, of shape `(n_clusters, 3)` when color quantizing. Can be overridden by `clusters`
+ in `preprocess`.
+ do_color_quantize (`bool`, *optional*, defaults to `self.do_color_quantize`):
+ Controls whether to apply color quantization to convert continuous pixel values to discrete cluster indices.
+ When True, each pixel is assigned to its nearest color cluster, enabling ImageGPT's discrete token modeling.
+ """
+
+ clusters: Union[np.ndarray, list[list[int]], "torch.Tensor"] | None
+ do_color_quantize: bool
+
+
+def squared_euclidean_distance_torch(a: torch.Tensor, b: torch.Tensor) -> torch.Tensor:
+ """
+ Compute squared Euclidean distances between all pixels and clusters.
+
+ Args:
+ a: (N, 3) tensor of pixel RGB values
+ b: (M, 3) tensor of cluster RGB values
+
+ Returns:
+ (N, M) tensor of squared distances
+ """
+ b = b.t() # (3, M)
+ a2 = torch.sum(a**2, dim=1) # (N,)
+ b2 = torch.sum(b**2, dim=0) # (M,)
+ ab = torch.matmul(a, b) # (N, M)
+ d = a2[:, None] - 2 * ab + b2[None, :] # Squared Euclidean Distance: a^2 - 2ab + b^2
+ return d # (N, M) tensor of squared distances
+
+
+def color_quantize_torch(x: torch.Tensor, clusters: torch.Tensor) -> torch.Tensor:
+ """
+ Assign each pixel to its nearest color cluster.
+
+ Args:
+ x: (H*W, 3) tensor of flattened pixel RGB values
+ clusters: (n_clusters, 3) tensor of cluster RGB values
+
+ Returns:
+ (H*W,) tensor of cluster indices
+ """
+ d = squared_euclidean_distance_torch(x, clusters)
+ return torch.argmin(d, dim=1)
+
+
+@auto_docstring
+class ImageGPTImageProcessor(TorchvisionBackend):
+ model_input_names = ["input_ids"]
+ valid_kwargs = ImageGPTImageProcessorKwargs
+ resample = PILImageResampling.BILINEAR
+ do_color_quantize = True
+ clusters = None
+ image_mean = [0.5, 0.5, 0.5]
+ image_std = [0.5, 0.5, 0.5]
+ do_rescale = True
+ do_normalize = True
+ size = {"height": 256, "width": 256}
+ do_resize = True
+
+ def __init__(
+ self,
+ clusters: list | np.ndarray | torch.Tensor | None = None, # keep as arg for backwards compatibility
+ **kwargs: Unpack[ImageGPTImageProcessorKwargs],
+ ):
+ r"""
+ clusters (`np.ndarray` or `list[list[int]]` or `torch.Tensor`, *optional*):
+ The color clusters to use, of shape `(n_clusters, 3)` when color quantizing. Can be overridden by `clusters`
+ in `preprocess`.
+ """
+ clusters = torch.as_tensor(clusters, dtype=torch.float32) if clusters is not None else None
+ super().__init__(clusters=clusters, **kwargs)
+
+ def _preprocess(
+ self,
+ images: list["torch.Tensor"],
+ do_resize: bool,
+ size: SizeDict,
+ resample: "PILImageResampling | tvF.InterpolationMode | int | None",
+ do_center_crop: bool,
+ crop_size: SizeDict,
+ do_rescale: bool,
+ rescale_factor: float,
+ do_normalize: bool,
+ image_mean: float | list[float] | None,
+ image_std: float | list[float] | None,
+ disable_grouping: bool | None,
+ return_tensors: str | TensorType | None,
+ do_color_quantize: bool | None = None,
+ clusters: list | np.ndarray | torch.Tensor | None = None,
+ **kwargs,
+ ):
+ # Group images by size for batched resizing
+ grouped_images, grouped_images_index = group_images_by_shape(images, disable_grouping=disable_grouping)
+ resized_images_grouped = {}
+ for shape, stacked_images in grouped_images.items():
+ if do_resize:
+ stacked_images = self.resize(image=stacked_images, size=size, resample=resample)
+ resized_images_grouped[shape] = stacked_images
+ resized_images = reorder_images(resized_images_grouped, grouped_images_index)
+
+ # Group images by size for further processing
+ # Needed in case do_resize is False, or resize returns images with different sizes
+ grouped_images, grouped_images_index = group_images_by_shape(resized_images, disable_grouping=disable_grouping)
+ processed_images_grouped = {}
+ for shape, stacked_images in grouped_images.items():
+ if do_center_crop:
+ stacked_images = self.center_crop(stacked_images, crop_size)
+ # Fused rescale and normalize
+ stacked_images = self.rescale_and_normalize(
+ stacked_images, do_rescale, rescale_factor, do_normalize, image_mean, image_std
+ )
+ processed_images_grouped[shape] = stacked_images
+
+ pixel_values = reorder_images(processed_images_grouped, grouped_images_index)
+
+ # If color quantization is requested, perform it; otherwise return pixel values
+ if do_color_quantize:
+ # Prepare clusters
+ if clusters is None:
+ raise ValueError("Clusters must be provided for color quantization.")
+ # Convert to torch tensor if needed (clusters might be passed as list/numpy)
+ clusters_torch = (
+ torch.as_tensor(clusters, dtype=torch.float32) if not isinstance(clusters, torch.Tensor) else clusters
+ ).to(pixel_values[0].device, dtype=pixel_values[0].dtype)
+
+ # Group images by shape for batch processing
+ # We need to check if the pixel values are a tensor or a list of tensors
+ grouped_images, grouped_images_index = group_images_by_shape(
+ pixel_values, disable_grouping=disable_grouping
+ )
+ # Process each group
+ input_ids_grouped = {}
+
+ for shape, stacked_images in grouped_images.items():
+ input_ids = color_quantize_torch(
+ stacked_images.permute(0, 2, 3, 1).reshape(-1, 3), clusters_torch
+ ) # (B*H*W, C)
+ input_ids_grouped[shape] = input_ids.reshape(stacked_images.shape[0], -1).reshape(
+ stacked_images.shape[0], -1
+ ) # (B, H, W)
+
+ input_ids = reorder_images(input_ids_grouped, grouped_images_index)
+
+ return BatchFeature(data={"input_ids": input_ids}, tensor_type=return_tensors)
+
+ return BatchFeature(data={"pixel_values": pixel_values}, tensor_type=return_tensors)
+
+ def to_dict(self):
+ # Convert torch tensors to lists for JSON serialization
+ output = super().to_dict()
+ if output.get("clusters") is not None and isinstance(output["clusters"], torch.Tensor):
+ output["clusters"] = output["clusters"].tolist()
+
+ return output
+
+
+__all__ = ["ImageGPTImageProcessor"]
diff --git a/third_party/transformers/src/transformers/models/imagegpt/image_processing_pil_imagegpt.py b/third_party/transformers/src/transformers/models/imagegpt/image_processing_pil_imagegpt.py
new file mode 100644
index 0000000000000000000000000000000000000000..cc287faa351047c64c1c23b0646f75665c73c5ba
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/imagegpt/image_processing_pil_imagegpt.py
@@ -0,0 +1,155 @@
+# Copyright 2022 The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Image processor class for ImageGPT."""
+
+from typing import Union
+
+import numpy as np
+
+from ...image_processing_backends import PilBackend
+from ...image_processing_utils import BatchFeature
+from ...image_utils import (
+ PILImageResampling,
+ SizeDict,
+)
+from ...processing_utils import ImagesKwargs, Unpack
+from ...utils import (
+ TensorType,
+ auto_docstring,
+ is_torch_available,
+)
+
+
+if is_torch_available():
+ import torch
+
+
+def squared_euclidean_distance(a, b):
+ b = b.T
+ a2 = np.sum(np.square(a), axis=1)
+ b2 = np.sum(np.square(b), axis=0)
+ ab = np.matmul(a, b)
+ d = a2[:, None] - 2 * ab + b2[None, :]
+ return d
+
+
+def color_quantize(x, clusters):
+ x = x.reshape(-1, 3)
+ d = squared_euclidean_distance(x, clusters)
+ return np.argmin(d, axis=1)
+
+
+# Adapted from transformers.models.imagegpt.image_processing_imagegpt.ImageGPTImageProcessorKwargs
+class ImageGPTImageProcessorKwargs(ImagesKwargs, total=False):
+ r"""
+ clusters (`np.ndarray` or `list[list[int]]` or `torch.Tensor`, *optional*, defaults to `self.clusters`):
+ The color clusters to use, of shape `(n_clusters, 3)` when color quantizing. Can be overridden by `clusters`
+ in `preprocess`.
+ do_color_quantize (`bool`, *optional*, defaults to `self.do_color_quantize`):
+ Controls whether to apply color quantization to convert continuous pixel values to discrete cluster indices.
+ When True, each pixel is assigned to its nearest color cluster, enabling ImageGPT's discrete token modeling.
+ """
+
+ clusters: Union[np.ndarray, list[list[int]], "torch.Tensor"] | None
+ do_color_quantize: bool
+
+
+@auto_docstring
+class ImageGPTImageProcessorPil(PilBackend):
+ model_input_names = ["input_ids"]
+ valid_kwargs = ImageGPTImageProcessorKwargs
+ resample = PILImageResampling.BILINEAR
+ do_color_quantize = True
+ clusters = None
+ image_mean = [0.5, 0.5, 0.5]
+ image_std = [0.5, 0.5, 0.5]
+ do_rescale = True
+ do_normalize = True
+ size = {"height": 256, "width": 256}
+ do_resize = True
+
+ def __init__(
+ self,
+ clusters: "list | np.ndarray | torch.Tensor | None" = None, # keep as arg for backwards compatibility
+ **kwargs: Unpack[ImageGPTImageProcessorKwargs],
+ ):
+ r"""
+ clusters (`np.ndarray` or `list[list[int]]` or `torch.Tensor`, *optional*):
+ The color clusters to use, of shape `(n_clusters, 3)` when color quantizing. Can be overridden by `clusters`
+ in `preprocess`.
+ """
+ if clusters is not None:
+ clusters = np.array(clusters)
+ super().__init__(clusters=clusters, **kwargs)
+
+ def _preprocess(
+ self,
+ images: list[np.ndarray],
+ do_resize: bool,
+ size: SizeDict,
+ resample: "PILImageResampling | None",
+ do_rescale: bool,
+ rescale_factor: float,
+ do_normalize: bool,
+ image_mean: float | list[float] | None,
+ image_std: float | list[float] | None,
+ return_tensors: str | TensorType | None,
+ do_color_quantize: bool | None = None,
+ clusters: "list | np.ndarray | torch.Tensor | None" = None,
+ **kwargs,
+ ):
+ processed_images = []
+ for image in images:
+ if do_resize:
+ image = self.resize(image, size, resample)
+ if do_rescale:
+ image = self.rescale(image, rescale_factor)
+ if do_normalize:
+ image = self.normalize(image, image_mean, image_std)
+ processed_images.append(image)
+
+ # If color quantization is requested, perform it; otherwise return pixel values
+ if do_color_quantize:
+ # Prepare clusters
+ if clusters is None:
+ raise ValueError("Clusters must be provided for color quantization.")
+ # Convert to numpy array if needed
+ clusters_np = np.array(clusters) if not isinstance(clusters, np.ndarray) else clusters
+
+ # Stack channel-first images (B, C, H, W) and transpose to (B, H, W, C) for color quantization
+ images_array = np.array(processed_images)
+ images_hwc = images_array.transpose(0, 2, 3, 1)
+ input_ids = color_quantize(images_hwc, clusters_np).reshape(
+ images_array.shape[0], images_array.shape[2], images_array.shape[3]
+ )
+
+ # flatten to (batch_size, height*width)
+ batch_size = input_ids.shape[0]
+ input_ids = input_ids.reshape(batch_size, -1)
+
+ # We need to convert back to a list to keep consistent behaviour across processors.
+ input_ids = list(input_ids)
+ return BatchFeature(data={"input_ids": input_ids}, tensor_type=return_tensors)
+
+ return BatchFeature(data={"pixel_values": processed_images}, tensor_type=return_tensors)
+
+ def to_dict(self):
+ output = super().to_dict()
+ if output.get("clusters") is not None and isinstance(output["clusters"], np.ndarray | torch.Tensor):
+ output["clusters"] = output["clusters"].tolist()
+
+ return output
+
+
+__all__ = ["ImageGPTImageProcessorPil"]
diff --git a/third_party/transformers/src/transformers/models/imagegpt/modeling_imagegpt.py b/third_party/transformers/src/transformers/models/imagegpt/modeling_imagegpt.py
new file mode 100644
index 0000000000000000000000000000000000000000..299047a2689551251c9e099ae9abf8a94331cdbd
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/imagegpt/modeling_imagegpt.py
@@ -0,0 +1,831 @@
+# Copyright 2021 The OpenAI Team Authors and HuggingFace Inc. team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""PyTorch OpenAI ImageGPT model."""
+
+import math
+from typing import Any
+
+import torch
+from torch import nn
+from torch.nn import CrossEntropyLoss
+
+from ... import initialization as init
+from ...activations import ACT2FN
+from ...cache_utils import Cache, DynamicCache, EncoderDecoderCache
+from ...generation import GenerationMixin
+from ...modeling_layers import GradientCheckpointingLayer
+from ...modeling_outputs import (
+ BaseModelOutputWithPastAndCrossAttentions,
+ CausalLMOutputWithCrossAttentions,
+ SequenceClassifierOutputWithPast,
+)
+from ...modeling_utils import PreTrainedModel
+from ...pytorch_utils import Conv1D
+from ...utils import (
+ auto_docstring,
+ logging,
+ torch_float,
+)
+from ...utils.generic import maybe_autocast
+from .configuration_imagegpt import ImageGPTConfig
+
+
+logger = logging.get_logger(__name__)
+
+
+class ImageGPTLayerNorm(nn.Module):
+ def __init__(self, hidden_size: tuple[int], eps: float = 1e-5):
+ super().__init__()
+ self.eps = eps
+ self.weight = nn.Parameter(torch.Tensor(hidden_size))
+
+ def forward(self, tensor: torch.Tensor) -> torch.Tensor:
+ # input is not mean centered
+ tensor = tensor / torch.sqrt(torch.mean(torch.square(tensor), axis=-1, keepdim=True) + self.eps)
+ tensor = tensor * self.weight
+ return tensor
+
+
+class ImageGPTAttention(nn.Module):
+ def __init__(self, config, is_cross_attention: bool | None = False, layer_idx: int | None = None):
+ super().__init__()
+ self.config = config
+ max_positions = config.max_position_embeddings
+ self.register_buffer(
+ "bias",
+ torch.tril(torch.ones((max_positions, max_positions), dtype=torch.bool)).view(
+ 1, 1, max_positions, max_positions
+ ),
+ persistent=False,
+ )
+
+ self.embed_dim = config.hidden_size
+ self.num_heads = config.num_attention_heads
+ self.head_dim = self.embed_dim // self.num_heads
+ self.split_size = self.embed_dim
+ if self.head_dim * self.num_heads != self.embed_dim:
+ raise ValueError(
+ f"`embed_dim` must be divisible by num_heads (got `embed_dim`: {self.embed_dim} and `num_heads`:"
+ f" {self.num_heads})."
+ )
+
+ self.scale_attn_weights = config.scale_attn_weights
+ self.is_cross_attention = is_cross_attention
+
+ # Layer-wise attention scaling, reordering, and upcasting
+ self.scale_attn_by_inverse_layer_idx = config.scale_attn_by_inverse_layer_idx
+ self.layer_idx = layer_idx
+ self.reorder_and_upcast_attn = config.reorder_and_upcast_attn
+
+ if self.is_cross_attention:
+ self.c_attn = Conv1D(2 * self.embed_dim, self.embed_dim)
+ self.q_attn = Conv1D(self.embed_dim, self.embed_dim)
+ else:
+ self.c_attn = Conv1D(3 * self.embed_dim, self.embed_dim)
+ self.c_proj = Conv1D(self.embed_dim, self.embed_dim)
+
+ self.attn_dropout = nn.Dropout(config.attn_pdrop)
+ self.resid_dropout = nn.Dropout(config.resid_pdrop)
+
+ def _attn(self, query, key, value, attention_mask=None):
+ attn_weights = torch.matmul(query, key.transpose(-1, -2))
+
+ if self.scale_attn_weights:
+ attn_weights = attn_weights / torch_float(value.size(-1) ** 0.5)
+
+ # Layer-wise attention scaling
+ if self.scale_attn_by_inverse_layer_idx:
+ attn_weights = attn_weights / float(self.layer_idx + 1)
+
+ if not self.is_cross_attention:
+ # if only "normal" attention layer implements causal mask
+ query_length, key_length = query.size(-2), key.size(-2)
+ causal_mask = self.bias[:, :, key_length - query_length : key_length, :key_length]
+ mask_value = torch.finfo(attn_weights.dtype).min
+ # Need to be a tensor, otherwise we get error: `RuntimeError: expected scalar type float but found double`.
+ # Need to be on the same device, otherwise `RuntimeError: ..., x and y to be on the same device`
+ mask_value = torch.tensor(mask_value, dtype=attn_weights.dtype, device=attn_weights.device)
+ attn_weights = torch.where(causal_mask, attn_weights, mask_value)
+
+ if attention_mask is not None:
+ # Apply the attention mask
+ attn_weights = attn_weights + attention_mask
+
+ attn_weights = nn.Softmax(dim=-1)(attn_weights)
+
+ # Downcast (if necessary) back to V's dtype (if in mixed-precision) -- No-Op otherwise
+ attn_weights = attn_weights.type(value.dtype)
+ attn_weights = self.attn_dropout(attn_weights)
+
+ attn_output = torch.matmul(attn_weights, value)
+
+ return attn_output, attn_weights
+
+ def _upcast_and_reordered_attn(self, query, key, value, attention_mask=None):
+ # Use `torch.baddbmm` (a bit more efficient w/ alpha param for scaling -- from Megatron-LM)
+ bsz, num_heads, q_seq_len, dk = query.size()
+ _, _, k_seq_len, _ = key.size()
+
+ # Preallocate attn_weights for `baddbmm`
+ attn_weights = torch.empty(bsz * num_heads, q_seq_len, k_seq_len, dtype=torch.float32, device=query.device)
+
+ # Compute Scale Factor
+ scale_factor = 1.0
+ if self.scale_attn_weights:
+ scale_factor /= float(value.size(-1)) ** 0.5
+
+ if self.scale_attn_by_inverse_layer_idx:
+ scale_factor /= float(self.layer_idx + 1)
+
+ # Upcast (turn off autocast) and reorder (Scale K by 1 / root(dk))
+ with maybe_autocast(query.device.type, enabled=False):
+ q, k = query.reshape(-1, q_seq_len, dk), key.transpose(-1, -2).reshape(-1, dk, k_seq_len)
+ attn_weights = torch.baddbmm(attn_weights, q.float(), k.float(), beta=0, alpha=scale_factor)
+ attn_weights = attn_weights.reshape(bsz, num_heads, q_seq_len, k_seq_len)
+
+ if not self.is_cross_attention:
+ # if only "normal" attention layer implements causal mask
+ query_length, key_length = query.size(-2), key.size(-2)
+ causal_mask = self.bias[:, :, key_length - query_length : key_length, :key_length]
+ mask_value = torch.finfo(attn_weights.dtype).min
+ # Need to be a tensor, otherwise we get error: `RuntimeError: expected scalar type float but found double`.
+ # Need to be on the same device, otherwise `RuntimeError: ..., x and y to be on the same device`
+ mask_value = torch.tensor(mask_value, dtype=attn_weights.dtype, device=attn_weights.device)
+ attn_weights = torch.where(causal_mask, attn_weights, mask_value)
+
+ if attention_mask is not None:
+ # Apply the attention mask
+ attn_weights = attn_weights + attention_mask
+
+ attn_weights = nn.Softmax(dim=-1)(attn_weights)
+
+ # Downcast (if necessary) back to V's dtype (if in mixed-precision) -- No-Op if otherwise
+ if attn_weights.dtype != torch.float32:
+ raise RuntimeError("Error with upcasting, attn_weights does not have dtype torch.float32")
+ attn_weights = attn_weights.type(value.dtype)
+ attn_weights = self.attn_dropout(attn_weights)
+
+ attn_output = torch.matmul(attn_weights, value)
+
+ return attn_output, attn_weights
+
+ def _split_heads(self, tensor, num_heads, attn_head_size):
+ """
+ Splits hidden_size dim into attn_head_size and num_heads
+ """
+ new_shape = tensor.size()[:-1] + (num_heads, attn_head_size)
+ tensor = tensor.view(*new_shape)
+ return tensor.permute(0, 2, 1, 3) # (batch, head, seq_length, head_features)
+
+ def _merge_heads(self, tensor, num_heads, attn_head_size):
+ """
+ Merges attn_head_size dim and num_attn_heads dim into hidden_size
+ """
+ tensor = tensor.permute(0, 2, 1, 3).contiguous()
+ new_shape = tensor.size()[:-2] + (num_heads * attn_head_size,)
+ return tensor.view(new_shape)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ layer_past: Cache | None = None,
+ attention_mask: torch.Tensor | None = None,
+ encoder_hidden_states: torch.Tensor | None = None,
+ encoder_attention_mask: torch.Tensor | None = None,
+ use_cache: bool | None = False,
+ output_attentions: bool | None = False,
+ **kwargs,
+ ) -> tuple:
+ is_cross_attention = encoder_hidden_states is not None
+ bsz, seq_len, _ = hidden_states.shape
+
+ if layer_past is not None:
+ if isinstance(layer_past, EncoderDecoderCache):
+ is_updated = layer_past.is_updated.get(self.layer_idx)
+ if is_cross_attention:
+ # after the first generated id, we can subsequently re-use all key/value_states from cache
+ curr_past_key_values = layer_past.cross_attention_cache
+ else:
+ curr_past_key_values = layer_past.self_attention_cache
+ else:
+ curr_past_key_values = layer_past
+
+ current_states = encoder_hidden_states if is_cross_attention else hidden_states
+ if is_cross_attention:
+ if not hasattr(self, "q_attn"):
+ raise ValueError(
+ "If class is used as cross attention, the weights `q_attn` have to be defined. "
+ "Please make sure to instantiate class with `ImageGPTAttention(..., is_cross_attention=True)`."
+ )
+
+ if layer_past is not None and is_updated:
+ # reuse k,v, cross_attentions, and compute only q
+ query = self.q_attn(hidden_states)
+ key = curr_past_key_values.layers[self.layer_idx].keys
+ value = curr_past_key_values.layers[self.layer_idx].values
+ else:
+ query = self.q_attn(hidden_states)
+ key, value = self.c_attn(current_states).split(self.split_size, dim=2)
+ key = key.view(bsz, -1, self.num_heads, self.head_dim).transpose(1, 2)
+ value = value.view(bsz, -1, self.num_heads, self.head_dim).transpose(1, 2)
+ else:
+ query, key, value = self.c_attn(current_states).split(self.split_size, dim=2)
+ key = key.view(bsz, -1, self.num_heads, self.head_dim).transpose(1, 2)
+ value = value.view(bsz, -1, self.num_heads, self.head_dim).transpose(1, 2)
+
+ if layer_past is not None:
+ # save all key/value_states to cache to be re-used for fast auto-regressive generation
+ key, value = curr_past_key_values.update(key, value, self.layer_idx)
+ # set flag that curr layer for cross-attn is already updated so we can re-use in subsequent calls
+ if is_cross_attention:
+ layer_past.is_updated[self.layer_idx] = True
+
+ query = query.view(bsz, seq_len, self.num_heads, self.head_dim).transpose(1, 2)
+
+ if self.reorder_and_upcast_attn:
+ attn_output, attn_weights = self._upcast_and_reordered_attn(query, key, value, attention_mask)
+ else:
+ attn_output, attn_weights = self._attn(query, key, value, attention_mask)
+
+ attn_output = self._merge_heads(attn_output, self.num_heads, self.head_dim)
+ attn_output = self.c_proj(attn_output)
+ attn_output = self.resid_dropout(attn_output)
+
+ return attn_output, attn_weights
+
+
+class ImageGPTMLP(nn.Module):
+ def __init__(self, intermediate_size, config):
+ super().__init__()
+ embed_dim = config.hidden_size
+ self.c_fc = Conv1D(intermediate_size, embed_dim)
+ self.c_proj = Conv1D(embed_dim, intermediate_size)
+ self.act = ACT2FN[config.activation_function]
+ self.dropout = nn.Dropout(config.resid_pdrop)
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ hidden_states = self.c_fc(hidden_states)
+ hidden_states = self.act(hidden_states)
+ hidden_states = self.c_proj(hidden_states)
+ hidden_states = self.dropout(hidden_states)
+ return hidden_states
+
+
+class ImageGPTBlock(GradientCheckpointingLayer):
+ def __init__(self, config, layer_idx=None):
+ super().__init__()
+ hidden_size = config.hidden_size
+ inner_dim = config.n_inner if config.n_inner is not None else 4 * hidden_size
+
+ self.ln_1 = ImageGPTLayerNorm(hidden_size, eps=config.layer_norm_epsilon)
+ self.attn = ImageGPTAttention(config, layer_idx=layer_idx)
+ self.ln_2 = ImageGPTLayerNorm(hidden_size, eps=config.layer_norm_epsilon)
+
+ if config.add_cross_attention:
+ self.crossattention = ImageGPTAttention(config, is_cross_attention=True, layer_idx=layer_idx)
+ self.ln_cross_attn = ImageGPTLayerNorm(hidden_size, eps=config.layer_norm_epsilon)
+
+ self.mlp = ImageGPTMLP(inner_dim, config)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ layer_past: Cache | None = None,
+ attention_mask: torch.Tensor | None = None,
+ encoder_hidden_states: torch.Tensor | None = None,
+ encoder_attention_mask: torch.Tensor | None = None,
+ use_cache: bool | None = False,
+ output_attentions: bool | None = False,
+ **kwargs,
+ ) -> tuple:
+ residual = hidden_states
+ hidden_states = self.ln_1(hidden_states)
+ attn_outputs = self.attn(
+ hidden_states,
+ layer_past=layer_past,
+ attention_mask=attention_mask,
+ use_cache=use_cache,
+ output_attentions=output_attentions,
+ )
+ attn_output = attn_outputs[0]
+ outputs = attn_outputs[1:]
+ # residual connection
+ hidden_states = attn_output + residual
+
+ if encoder_hidden_states is not None:
+ # add one self-attention block for cross-attention
+ if not hasattr(self, "crossattention"):
+ raise ValueError(
+ f"If `encoder_hidden_states` are passed, {self} has to be instantiated with "
+ "cross-attention layers by setting `config.add_cross_attention=True`"
+ )
+ residual = hidden_states
+ hidden_states = self.ln_cross_attn(hidden_states)
+ cross_attn_outputs = self.crossattention(
+ hidden_states,
+ layer_past=layer_past,
+ attention_mask=attention_mask,
+ encoder_hidden_states=encoder_hidden_states,
+ encoder_attention_mask=encoder_attention_mask,
+ output_attentions=output_attentions,
+ )
+ attn_output = cross_attn_outputs[0]
+ # residual connection
+ hidden_states = residual + attn_output
+ outputs = outputs + cross_attn_outputs[1:] # add cross attentions if we output attention weights
+
+ residual = hidden_states
+ hidden_states = self.ln_2(hidden_states)
+ feed_forward_hidden_states = self.mlp(hidden_states)
+ # residual connection
+ hidden_states = residual + feed_forward_hidden_states
+
+ return (hidden_states,) + outputs
+
+
+@auto_docstring
+class ImageGPTPreTrainedModel(PreTrainedModel):
+ config: ImageGPTConfig
+ base_model_prefix = "transformer"
+ main_input_name = "input_ids"
+ input_modalities = ("image",)
+ supports_gradient_checkpointing = True
+ _no_split_modules = ["ImageGPTBlock"]
+
+ @torch.no_grad()
+ def _init_weights(self, module):
+ """Initialize the weights."""
+ super()._init_weights(module)
+
+ # Reinitialize selected weights subject to the OpenAI GPT-2 Paper Scheme:
+ # > A modified initialization which accounts for the accumulation on the residual path with model depth. Scale
+ # > the weights of residual layers at initialization by a factor of 1/√N where N is the # of residual layers.
+ # > -- GPT-2 :: https://openai.com/blog/better-language-models/
+ #
+ # Reference (Megatron-LM): https://github.com/NVIDIA/Megatron-LM/blob/main/megatron/model/gpt_model.py
+ if isinstance(module, PreTrainedModel):
+ for name, p in module.named_parameters():
+ if "c_proj" in name and "weight" in name:
+ # Special Scaled Initialization --> There are 2 Layer Norms per Transformer Block
+ init.normal_(p, mean=0.0, std=self.config.initializer_range / math.sqrt(2 * self.config.n_layer))
+ elif isinstance(module, ImageGPTAttention):
+ max_positions = module.config.max_position_embeddings
+ init.copy_(
+ module.bias,
+ torch.tril(torch.ones((max_positions, max_positions), dtype=torch.bool)).view(
+ 1, 1, max_positions, max_positions
+ ),
+ )
+
+
+@auto_docstring
+class ImageGPTModel(ImageGPTPreTrainedModel):
+ def __init__(self, config: ImageGPTConfig):
+ super().__init__(config)
+
+ self.embed_dim = config.hidden_size
+
+ self.wte = nn.Embedding(config.vocab_size, self.embed_dim)
+ self.wpe = nn.Embedding(config.max_position_embeddings, self.embed_dim)
+
+ self.drop = nn.Dropout(config.embd_pdrop)
+ self.h = nn.ModuleList([ImageGPTBlock(config, layer_idx=i) for i in range(config.num_hidden_layers)])
+ self.ln_f = ImageGPTLayerNorm(self.embed_dim, eps=config.layer_norm_epsilon)
+
+ self.gradient_checkpointing = False
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ def get_input_embeddings(self):
+ return self.wte
+
+ def set_input_embeddings(self, new_embeddings):
+ self.wte = new_embeddings
+
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.Tensor | None = None,
+ past_key_values: Cache | None = None,
+ attention_mask: torch.Tensor | None = None,
+ token_type_ids: torch.Tensor | None = None,
+ position_ids: torch.Tensor | None = None,
+ inputs_embeds: torch.Tensor | None = None,
+ encoder_hidden_states: torch.Tensor | None = None,
+ encoder_attention_mask: torch.Tensor | None = None,
+ use_cache: bool | None = None,
+ output_attentions: bool | None = None,
+ output_hidden_states: bool | None = None,
+ return_dict: bool | None = None,
+ **kwargs: Any,
+ ) -> tuple | BaseModelOutputWithPastAndCrossAttentions:
+ r"""
+ input_ids (`torch.LongTensor` of shape `(batch_size, input_ids_length)`):
+ `input_ids_length` = `sequence_length` if `past_key_values` is `None` else
+ `past_key_values.get_seq_length()` (`sequence_length` of input past key value states). Indices of input
+ sequence tokens in the vocabulary.
+
+ If `past_key_values` is used, only `input_ids` that do not have their past calculated should be passed as
+ `input_ids`.
+
+ Indices can be obtained using [`AutoImageProcessor`]. See [`ImageGPTImageProcessor.__call__`] for details.
+
+ Examples:
+
+ ```python
+ >>> from transformers import AutoImageProcessor, ImageGPTModel
+ >>> from PIL import Image
+ >>> import httpx
+ >>> from io import BytesIO
+
+ >>> url = "http://images.cocodataset.org/val2017/000000039769.jpg"
+ >>> with httpx.stream("GET", url) as response:
+ ... image = Image.open(BytesIO(response.read()))
+
+ >>> image_processor = AutoImageProcessor.from_pretrained("openai/imagegpt-small")
+ >>> model = ImageGPTModel.from_pretrained("openai/imagegpt-small")
+
+ >>> inputs = image_processor(images=image, return_tensors="pt")
+ >>> outputs = model(**inputs)
+ >>> last_hidden_states = outputs.last_hidden_state
+ ```"""
+
+ 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
+ )
+ use_cache = use_cache if use_cache is not None else self.config.use_cache
+ return_dict = return_dict if return_dict is not None else self.config.return_dict
+
+ if input_ids is not None and inputs_embeds is not None:
+ raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time")
+ elif input_ids is not None:
+ self.warn_if_padding_and_no_attention_mask(input_ids, attention_mask)
+ input_shape = input_ids.size()
+ input_ids = input_ids.view(-1, input_shape[-1])
+ batch_size = input_ids.shape[0]
+ elif inputs_embeds is not None:
+ input_shape = inputs_embeds.size()[:-1]
+ batch_size = inputs_embeds.shape[0]
+ else:
+ raise ValueError("You have to specify either input_ids or inputs_embeds")
+
+ device = input_ids.device if input_ids is not None else inputs_embeds.device
+
+ if self.gradient_checkpointing and self.training:
+ if use_cache:
+ logger.warning_once(
+ "`use_cache=True` is incompatible with gradient checkpointing. Setting `use_cache=False`..."
+ )
+ use_cache = False
+
+ if token_type_ids is not None:
+ token_type_ids = token_type_ids.view(-1, input_shape[-1])
+
+ if use_cache and past_key_values is None:
+ past_key_values = DynamicCache(config=self.config)
+
+ if position_ids is None:
+ past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
+ position_ids = torch.arange(input_shape[-1], device=device) + past_seen_tokens
+ position_ids = position_ids.unsqueeze(0)
+
+ # ImageGPTAttention mask.
+ if attention_mask is not None:
+ if batch_size <= 0:
+ raise ValueError("batch_size has to be defined and > 0")
+ attention_mask = attention_mask.view(batch_size, -1)
+ # We create a 3D attention mask from a 2D tensor mask.
+ # Sizes are [batch_size, 1, 1, to_seq_length]
+ # So we can broadcast to [batch_size, num_heads, from_seq_length, to_seq_length]
+ # this attention mask is more simple than the triangular masking of causal attention
+ # used in OpenAI GPT, we just need to prepare the broadcast dimension here.
+ attention_mask = attention_mask[:, None, None, :]
+
+ # Since attention_mask is 1.0 for positions we want to attend and 0.0 for
+ # masked positions, this operation will create a tensor which is 0.0 for
+ # positions we want to attend and the dtype's smallest value for masked positions.
+ # Since we are adding it to the raw scores before the softmax, this is
+ # effectively the same as removing these entirely.
+ attention_mask = attention_mask.to(dtype=self.dtype) # fp16 compatibility
+ attention_mask = (1.0 - attention_mask) * torch.finfo(self.dtype).min
+
+ # If a 2D or 3D attention mask is provided for the cross-attention
+ # we need to make broadcastable to [batch_size, num_heads, seq_length, seq_length]
+ if self.config.add_cross_attention and encoder_hidden_states is not None:
+ encoder_batch_size, encoder_sequence_length, _ = encoder_hidden_states.size()
+ encoder_hidden_shape = (encoder_batch_size, encoder_sequence_length)
+ if encoder_attention_mask is None:
+ encoder_attention_mask = torch.ones(encoder_hidden_shape, device=device)
+ encoder_attention_mask = self.invert_attention_mask(encoder_attention_mask)
+ else:
+ encoder_attention_mask = None
+
+ if inputs_embeds is None:
+ inputs_embeds = self.wte(input_ids)
+ position_embeds = self.wpe(position_ids)
+ hidden_states = inputs_embeds + position_embeds.to(inputs_embeds.device)
+
+ if token_type_ids is not None:
+ token_type_embeds = self.wte(token_type_ids)
+ hidden_states = hidden_states + token_type_embeds
+
+ hidden_states = self.drop(hidden_states)
+ output_shape = input_shape + (hidden_states.size(-1),)
+
+ all_self_attentions = () if output_attentions else None
+ all_cross_attentions = () if output_attentions and self.config.add_cross_attention else None
+ all_hidden_states = () if output_hidden_states else None
+ for i, block in enumerate(self.h):
+ if output_hidden_states:
+ all_hidden_states = all_hidden_states + (hidden_states,)
+
+ outputs = block(
+ hidden_states,
+ past_key_values,
+ attention_mask,
+ encoder_hidden_states, # as a positional argument for gradient checkpointing
+ encoder_attention_mask=encoder_attention_mask,
+ use_cache=use_cache,
+ output_attentions=output_attentions,
+ )
+
+ hidden_states = outputs[0]
+ if output_attentions:
+ all_self_attentions = all_self_attentions + (outputs[1],)
+ if self.config.add_cross_attention:
+ all_cross_attentions = all_cross_attentions + (outputs[2],)
+
+ hidden_states = self.ln_f(hidden_states)
+ hidden_states = hidden_states.view(*output_shape)
+
+ # Add last hidden state
+ if output_hidden_states:
+ all_hidden_states = all_hidden_states + (hidden_states,)
+
+ if not return_dict:
+ return tuple(
+ v
+ for v in [hidden_states, past_key_values, all_hidden_states, all_self_attentions, all_cross_attentions]
+ if v is not None
+ )
+
+ return BaseModelOutputWithPastAndCrossAttentions(
+ last_hidden_state=hidden_states,
+ past_key_values=past_key_values,
+ hidden_states=all_hidden_states,
+ attentions=all_self_attentions,
+ cross_attentions=all_cross_attentions,
+ )
+
+
+@auto_docstring(
+ custom_intro="""
+ The ImageGPT Model transformer with a language modeling head on top (linear layer with weights tied to the input
+ embeddings).
+ """
+)
+class ImageGPTForCausalImageModeling(ImageGPTPreTrainedModel, GenerationMixin):
+ _tied_weights_keys = {"lm_head.weight": "transformer.wte.weight"}
+
+ def __init__(self, config: ImageGPTConfig):
+ super().__init__(config)
+ self.transformer = ImageGPTModel(config)
+ self.lm_head = nn.Linear(config.n_embd, config.vocab_size - 1, bias=False)
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.Tensor | None = None,
+ past_key_values: Cache | None = None,
+ attention_mask: torch.Tensor | None = None,
+ token_type_ids: torch.Tensor | None = None,
+ position_ids: torch.Tensor | None = None,
+ inputs_embeds: torch.Tensor | None = None,
+ encoder_hidden_states: torch.Tensor | None = None,
+ encoder_attention_mask: torch.Tensor | None = None,
+ labels: torch.Tensor | None = None,
+ use_cache: bool | None = None,
+ output_attentions: bool | None = None,
+ output_hidden_states: bool | None = None,
+ return_dict: bool | None = None,
+ **kwargs: Any,
+ ) -> tuple | CausalLMOutputWithCrossAttentions:
+ r"""
+ input_ids (`torch.LongTensor` of shape `(batch_size, input_ids_length)`):
+ `input_ids_length` = `sequence_length` if `past_key_values` is `None` else
+ `past_key_values.get_seq_length()` (`sequence_length` of input past key value states). Indices of input
+ sequence tokens in the vocabulary.
+
+ If `past_key_values` is used, only `input_ids` that do not have their past calculated should be passed as
+ `input_ids`.
+
+ Indices can be obtained using [`AutoImageProcessor`]. See [`ImageGPTImageProcessor.__call__`] for details.
+ labels (`torch.LongTensor` of shape `(batch_size, input_ids_length)`, *optional*):
+ Labels for language modeling. Note that the labels **are shifted** inside the model, i.e. you can set
+ `labels = input_ids` Indices are selected in `[-100, 0, ..., config.vocab_size]` All labels set to `-100`
+ are ignored (masked), the loss is only computed for labels in `[0, ..., config.vocab_size]`
+
+ Examples:
+
+ ```python
+ >>> from transformers import AutoImageProcessor, ImageGPTForCausalImageModeling
+ >>> import torch
+ >>> import matplotlib.pyplot as plt
+ >>> import numpy as np
+
+ >>> image_processor = AutoImageProcessor.from_pretrained("openai/imagegpt-small")
+ >>> model = ImageGPTForCausalImageModeling.from_pretrained("openai/imagegpt-small")
+ >>> device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
+ >>> model.to(device) # doctest: +IGNORE_RESULT
+
+ >>> # unconditional generation of 8 images
+ >>> batch_size = 4
+ >>> context = torch.full((batch_size, 1), model.config.vocab_size - 1) # initialize with SOS token
+ >>> context = context.to(device)
+ >>> output = model.generate(
+ ... input_ids=context, max_length=model.config.n_positions + 1, temperature=1.0, do_sample=True, top_k=40
+ ... )
+
+ >>> clusters = image_processor.clusters
+ >>> height = image_processor.size["height"]
+ >>> width = image_processor.size["width"]
+
+ >>> samples = output[:, 1:].detach().cpu().numpy()
+ >>> samples_img = [
+ ... np.reshape(np.rint(127.5 * (clusters[s] + 1.0)), [height, width, 3]).astype(np.uint8) for s in samples
+ ... ] # convert color cluster tokens back to pixels
+ >>> f, axes = plt.subplots(1, batch_size, dpi=300)
+
+ >>> for img, ax in zip(samples_img, axes): # doctest: +IGNORE_RESULT
+ ... ax.axis("off")
+ ... ax.imshow(img)
+ ```"""
+
+ return_dict = return_dict if return_dict is not None else self.config.return_dict
+
+ transformer_outputs = self.transformer(
+ input_ids,
+ past_key_values=past_key_values,
+ attention_mask=attention_mask,
+ token_type_ids=token_type_ids,
+ position_ids=position_ids,
+ inputs_embeds=inputs_embeds,
+ encoder_hidden_states=encoder_hidden_states,
+ encoder_attention_mask=encoder_attention_mask,
+ use_cache=use_cache,
+ output_attentions=output_attentions,
+ output_hidden_states=output_hidden_states,
+ return_dict=return_dict,
+ )
+ hidden_states = transformer_outputs[0]
+
+ lm_logits = self.lm_head(hidden_states)
+
+ loss = None
+ if labels is not None:
+ # Shift so that tokens < n predict n
+ shift_logits = lm_logits[..., :-1, :].contiguous()
+ shift_labels = labels[..., 1:].contiguous()
+ # Flatten the tokens
+ loss_fct = CrossEntropyLoss()
+ loss = loss_fct(shift_logits.view(-1, shift_logits.size(-1)), shift_labels.view(-1))
+
+ if not return_dict:
+ output = (lm_logits,) + transformer_outputs[1:]
+ return ((loss,) + output) if loss is not None else output
+
+ return CausalLMOutputWithCrossAttentions(
+ loss=loss,
+ logits=lm_logits,
+ past_key_values=transformer_outputs.past_key_values,
+ hidden_states=transformer_outputs.hidden_states,
+ attentions=transformer_outputs.attentions,
+ cross_attentions=transformer_outputs.cross_attentions,
+ )
+
+
+@auto_docstring(
+ custom_intro="""
+ The ImageGPT Model transformer with an image classification head on top (linear layer).
+ [`ImageGPTForImageClassification`] average-pools the hidden states in order to do the classification.
+ """
+)
+class ImageGPTForImageClassification(ImageGPTPreTrainedModel):
+ def __init__(self, config: ImageGPTConfig):
+ super().__init__(config)
+ self.num_labels = config.num_labels
+ self.transformer = ImageGPTModel(config)
+ self.score = nn.Linear(config.n_embd, self.num_labels, bias=False)
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.Tensor | None = None,
+ past_key_values: Cache | None = None,
+ attention_mask: torch.Tensor | None = None,
+ token_type_ids: torch.Tensor | None = None,
+ position_ids: torch.Tensor | None = None,
+ inputs_embeds: torch.Tensor | None = None,
+ labels: torch.Tensor | None = None,
+ use_cache: bool | None = None,
+ output_attentions: bool | None = None,
+ output_hidden_states: bool | None = None,
+ return_dict: bool | None = None,
+ **kwargs: Any,
+ ) -> tuple | SequenceClassifierOutputWithPast:
+ r"""
+ input_ids (`torch.LongTensor` of shape `(batch_size, input_ids_length)`):
+ `input_ids_length` = `sequence_length` if `past_key_values` is `None` else
+ `past_key_values.get_seq_length()` (`sequence_length` of input past key value states). Indices of input
+ sequence tokens in the vocabulary.
+
+ If `past_key_values` is used, only `input_ids` that do not have their past calculated should be passed as
+ `input_ids`.
+
+ Indices can be obtained using [`AutoImageProcessor`]. See [`ImageGPTImageProcessor.__call__`] for details.
+ labels (`torch.LongTensor` of shape `(batch_size,)`, *optional*):
+ Labels for computing the sequence classification/regression loss. Indices should be in `[0, ...,
+ config.num_labels - 1]`. If `config.num_labels == 1` a regression loss is computed (Mean-Square loss), If
+ `config.num_labels > 1` a classification loss is computed (Cross-Entropy).
+
+ Examples:
+
+ ```python
+ >>> from transformers import AutoImageProcessor, ImageGPTForImageClassification
+ >>> from PIL import Image
+ >>> import httpx
+ >>> from io import BytesIO
+
+ >>> url = "http://images.cocodataset.org/val2017/000000039769.jpg"
+ >>> with httpx.stream("GET", url) as response:
+ ... image = Image.open(BytesIO(response.read()))
+
+ >>> image_processor = AutoImageProcessor.from_pretrained("openai/imagegpt-small")
+ >>> model = ImageGPTForImageClassification.from_pretrained("openai/imagegpt-small")
+
+ >>> inputs = image_processor(images=image, return_tensors="pt")
+ >>> outputs = model(**inputs)
+ >>> logits = outputs.logits
+ ```"""
+
+ return_dict = return_dict if return_dict is not None else self.config.return_dict
+
+ transformer_outputs = self.transformer(
+ input_ids,
+ past_key_values=past_key_values,
+ attention_mask=attention_mask,
+ token_type_ids=token_type_ids,
+ position_ids=position_ids,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ output_attentions=output_attentions,
+ output_hidden_states=output_hidden_states,
+ return_dict=return_dict,
+ )
+ hidden_states = transformer_outputs[0]
+ # average-pool the hidden states along the sequence dimension
+ pooled_hidden_states = hidden_states.mean(dim=1)
+ # project from (batch_size, hidden_size) to (batch_size, num_labels)
+ logits = self.score(pooled_hidden_states)
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(labels, logits, self.config)
+
+ if not return_dict:
+ output = (logits,) + transformer_outputs[1:]
+ return ((loss,) + output) if loss is not None else output
+
+ return SequenceClassifierOutputWithPast(
+ loss=loss,
+ logits=logits,
+ past_key_values=transformer_outputs.past_key_values,
+ hidden_states=transformer_outputs.hidden_states,
+ attentions=transformer_outputs.attentions,
+ )
+
+
+__all__ = [
+ "ImageGPTForCausalImageModeling",
+ "ImageGPTForImageClassification",
+ "ImageGPTModel",
+ "ImageGPTPreTrainedModel",
+]
diff --git a/third_party/transformers/src/transformers/models/janus/__init__.py b/third_party/transformers/src/transformers/models/janus/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..97bfb063dd5e858ab3a2d8a7c38096548a551427
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/janus/__init__.py
@@ -0,0 +1,30 @@
+# Copyright 2025 Deepseek AI and The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_janus import *
+ from .image_processing_janus import *
+ from .image_processing_pil_janus import *
+ from .modeling_janus import *
+ from .processing_janus import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/janus/configuration_janus.py b/third_party/transformers/src/transformers/models/janus/configuration_janus.py
new file mode 100644
index 0000000000000000000000000000000000000000..979ef5cfc4457a39f461381fdb875bda070c5222
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/janus/configuration_janus.py
@@ -0,0 +1,168 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/janus/modular_janus.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_janus.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2025 Deepseek AI and The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...utils import auto_docstring, logging
+from ..auto import CONFIG_MAPPING, AutoConfig
+
+
+logger = logging.get_logger(__name__)
+
+
+@auto_docstring(checkpoint="deepseek-community/Janus-Pro-1B")
+@strict
+class JanusVisionConfig(PreTrainedConfig):
+ r"""
+ projection_dropout (`float`, *optional*, defaults to 0.0):
+ Dropout probability for the projection layer.
+ num_image_tokens (`int`, *optional*, defaults to 576):
+ Number of image tokens.
+ """
+
+ model_type = "janus_vision_model"
+ base_config_key = "vision_config"
+
+ hidden_size: int = 1024
+ num_hidden_layers: int = 24
+ num_attention_heads: int = 16
+ num_channels: int = 3
+ image_size: int | list[int] | tuple[int, int] = 384
+ patch_size: int | list[int] | tuple[int, int] = 16
+ hidden_act: str = "gelu"
+ layer_norm_eps: float = 1e-6
+ attention_dropout: float | int = 0.0
+ mlp_ratio: float | int = 4.0
+ attention_bias: bool = True
+ hidden_dropout_rate: float | int = 0.0
+ projection_dim: int = 2048
+ projection_dropout: float | int = 0.0
+ use_qk_norm: bool = False
+ initializer_range: float = 0.02
+ depth: int = 2
+ num_image_tokens: int = 576
+
+
+@auto_docstring(checkpoint="deepseek-community/Janus-Pro-1B")
+@strict
+class JanusVQVAEConfig(PreTrainedConfig):
+ r"""
+ base_channels (`int`, *optional*, defaults to 128):
+ Base channel count.
+ channel_multiplier (`list[int]`, *optional*, defaults to `[1, 1, 2, 2, 4]`):
+ Channel multipliers for each resolution.
+ num_res_blocks (`int`, *optional*, defaults to 2):
+ Number of residual blocks.
+ num_patches (`int`, *optional*, defaults to 32):
+ Num of patches the input images can be divided into.
+ out_channels (`int`, *optional*, defaults to 3):
+ Number of out channels.
+ image_token_embed_dim (`int`, *optional*, defaults to 2048):
+ Dimension of image embeddings. It should be same as the dimensionality of text embeddings.
+ """
+
+ model_type = "janus_vqgan"
+ base_config_key = "vq_config"
+
+ embed_dim: int = 8
+ num_embeddings: int = 16384
+ double_latent: bool = False
+ latent_channels: int = 256
+ in_channels: int = 3
+ base_channels: int = 128
+ channel_multiplier: list[int] | tuple[int, ...] = (1, 1, 2, 2, 4)
+ num_res_blocks: int = 2
+ dropout: float | int = 0.0
+ initializer_range: float = 0.02
+ num_patches: int = 32
+ out_channels: int = 3
+ projection_dim: int = 2048
+ num_hidden_layers: int = 2
+ hidden_act: str = "gelu"
+ image_token_embed_dim: int = 2048
+
+
+@auto_docstring(checkpoint="deepseek-community/Janus-Pro-1B")
+@strict
+class JanusConfig(PreTrainedConfig):
+ r"""
+ Example:
+
+ ```python
+ >>> from transformers import JanusForConditionalGeneration, JanusConfig, JanusVisionConfig, JanusVQVAEConfig, LlamaConfig
+
+ >>> # Initializing a Janus vision config
+ >>> vision_config = JanusVisionConfig()
+
+ >>> # Initializing a Llama config
+ >>> text_config = LlamaConfig()
+
+ >>> # Initializing a VQ config
+ >>> vq_config = JanusVQVAEConfig()
+
+ >>> # Initializing a Janus Pro 1B style configuration
+ >>> configuration = JanusConfig(vision_config=vision_config, text_config=text_config, vq_config=vq_config)
+
+ >>> # Initializing a model from the Janus Pro 1B style configuration
+ >>> model = JanusForConditionalGeneration(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "janus"
+ sub_configs = {
+ "text_config": AutoConfig,
+ "vision_config": JanusVisionConfig,
+ "vq_config": JanusVQVAEConfig,
+ }
+
+ text_config: dict | PreTrainedConfig | None = None
+ vision_config: dict | PreTrainedConfig | None = None
+ vq_config: dict | PreTrainedConfig | None = None
+ image_token_id: int = 100581
+
+ def __post_init__(self, **kwargs):
+ if isinstance(self.text_config, dict):
+ self.text_config["model_type"] = self.text_config.get("model_type", "llama")
+ self.text_config = CONFIG_MAPPING[self.text_config["model_type"]](**self.text_config)
+ elif self.text_config is None:
+ logger.info("`text_config` is None. Initializing with default values")
+ self.text_config = CONFIG_MAPPING["llama"]()
+
+ if self.vision_config is None:
+ logger.info("`vision_config` is None. Initializing with default JanusVisionConfig values")
+ self.vision_config = JanusVisionConfig()
+ elif isinstance(self.vision_config, dict):
+ self.vision_config = JanusVisionConfig(**self.vision_config)
+
+ if self.vq_config is None:
+ logger.info("`vq_config` is None. Initializing with default JanusVQVAEConfig values")
+ self.vq_config = JanusVQVAEConfig()
+ elif isinstance(self.vq_config, dict):
+ self.vq_config = JanusVQVAEConfig(**self.vq_config)
+
+ # This dimension is required when decoding discrete image tokens to continuous input.
+ self.vq_config.num_patches = self.vision_config.image_size // self.vision_config.patch_size
+ super().__post_init__(**kwargs)
+
+
+__all__ = ["JanusVQVAEConfig", "JanusVisionConfig", "JanusConfig"]
diff --git a/third_party/transformers/src/transformers/models/janus/convert_janus_weights_to_hf.py b/third_party/transformers/src/transformers/models/janus/convert_janus_weights_to_hf.py
new file mode 100644
index 0000000000000000000000000000000000000000..f8a9a77338cd0dcba398226b8f9414f171c84822
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/janus/convert_janus_weights_to_hf.py
@@ -0,0 +1,497 @@
+# Copyright 2025 Deepseek AI and The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Example of run command (run from root):
+
+python src/transformers/models/janus/convert_janus_weights_to_hf.py --repo_id deepseek-ai/Janus-Pro-1B --local_dir tmp/hub_code_in --output_dir tmp/hub_code_out
+Using provided local directory: tmp/hub_code_in
+"""
+
+import argparse
+import gc
+import json
+import os
+import re
+
+import torch
+from huggingface_hub import snapshot_download
+
+from transformers import (
+ AutoTokenizer,
+ JanusConfig,
+ JanusForConditionalGeneration,
+ JanusVisionConfig,
+ JanusVQVAEConfig,
+ LlamaConfig,
+)
+from transformers.models.janus.image_processing_janus import JanusImageProcessor
+from transformers.models.janus.processing_janus import JanusProcessor
+
+
+# Mappings
+MAPPINGS = {
+ # Vision model
+ r"(?\b(vision_model|model\.vision_model)\b.*\.)proj(?=\.|\s|$)": r"\gprojection_layer",
+ r"(?P\b(vision_model|model\.vision_model)\b.*\.)norm(?=\.|\s|$)": r"\glayer_norm",
+ r"(?P\b(vision_model|model\.vision_model)\b.*\.)norm1(?=\.|\s|$)": r"\glayer_norm1",
+ r"(?P\b(vision_model|model\.vision_model)\b.*\.)norm2(?=\.|\s|$)": r"\glayer_norm2",
+ r"\bvision_model\.vision_tower\.attn_pool\.[^\s$]*": None,
+ # VQ Model
+ r"gen_vision_model": "model.vqmodel",
+ r"(?P\b(gen_vision_model|model\.vqmodel)\b.*\.)decoder\.conv_blocks(?=\.|\s|$)": r"\gdecoder.up",
+ r"(?P\b(gen_vision_model|model\.vqmodel)\b.*\.)encoder\.conv_blocks(?=\.|\s|$)": r"\gencoder.down",
+ r"(?P\b(gen_vision_model|model\.vqmodel)\b.*\.)res(?=\.|\s|$)": r"\gblock",
+ r"(?P\b(gen_vision_model|model\.vqmodel)\b.*\.)mid\.0(?=\.|\s|$)": r"\gmid.block_1",
+ r"(?P\b(gen_vision_model|model\.vqmodel)\b.*\.)mid\.1(?=\.|\s|$)": r"\gmid.attn_1",
+ r"(?P\b(gen_vision_model|model\.vqmodel)\b.*\.)mid\.2(?=\.|\s|$)": r"\gmid.block_2",
+ # Aligner Modules
+ r"(gen_aligner)\.layers\.0": r"model.generation_aligner.fc1",
+ r"(gen_aligner)\.layers\.2": r"model.generation_aligner.hidden_layers.0",
+ r"(?']%}"
+ "{%set i=0%}"
+ "{%for message in messages%}"
+ "{%if message['role']|lower=='user'%}"
+ "<|User|>: "
+ "{%elif message['role']|lower=='assistant'%}"
+ "<|Assistant|>:{%if not (loop.last and not add_generation_prompt and message['content'][0]['type']=='text' and message['content'][0]['text']=='')%} {%endif%}"
+ "{%else%}"
+ "{{message['role'].capitalize()}}: "
+ "{%endif%}"
+ "{%for content in message['content']%}"
+ "{%if content['type']=='image'%}"
+ "{%if not loop.first%}{{'\n'}}{%endif%}"
+ ""
+ "{%if not loop.last%}{{'\n'}}{%endif%}"
+ "{%elif content['type']=='text'%}"
+ "{%set text=content['text']%}"
+ "{%if loop.first%}{%set text=text.lstrip()%}{%endif%}"
+ "{%if loop.last%}{%set text=text.rstrip()%}{%endif%}"
+ "{%if not loop.first and message['content'][loop.index0-1]['type']=='text'%}"
+ "{{' '+text}}"
+ "{%else%}"
+ "{{text}}"
+ "{%endif%}"
+ "{%endif%}"
+ "{%endfor%}"
+ "{%if not loop.last or add_generation_prompt%}"
+ "{%if message['role']|lower=='user'%}"
+ "{{seps[0]}}"
+ "{%else%}"
+ "{{seps[1]}}"
+ "{%endif%}"
+ "{%endif%}"
+ "{%endfor%}"
+ "{%if add_generation_prompt%}<|Assistant|>:{%endif%}"
+)
+
+
+def convert_old_keys_to_new_keys(state_dict):
+ keys_as_text = "\n".join(state_dict.keys())
+ new_keys_as_text = keys_as_text
+ for old, repl in MAPPINGS.items():
+ if repl is None:
+ new_keys_as_text = re.sub(old, "", new_keys_as_text)
+ else:
+ new_keys_as_text = re.sub(old, repl, new_keys_as_text)
+ output_dict = dict(zip(keys_as_text.split("\n"), new_keys_as_text.split("\n")))
+ return output_dict
+
+
+def split_tensor(tensor, key):
+ """Splits a merged tensor (qkv or kv) into separate tensors and creates keys for each part."""
+
+ if "qkv" in key:
+ prefix_to_replace = "qkv"
+ num_splits = 3
+ new_keys = ["q_proj", "k_proj", "v_proj"]
+ elif "kv" in key:
+ prefix_to_replace = "kv"
+ num_splits = 2
+ new_keys = ["k_proj", "v_proj"]
+ else:
+ raise ValueError(f"Unrecognized tensor type in key: {key}")
+
+ split_size = tensor.shape[0] // num_splits
+ tensors = torch.split(tensor, split_size, dim=0)
+ return {key.replace(prefix_to_replace, new_keys[i]): tensors[i] for i in range(num_splits)}
+
+
+def convert_state_dict_to_hf(state_dict):
+ """Convert state dict keys to HF format."""
+ conversion_dict = convert_old_keys_to_new_keys(state_dict)
+ converted_state_dict = {}
+
+ for old_key, new_key in conversion_dict.items():
+ if new_key:
+ if "qkv" in new_key or "kv" in new_key: # Detect merged attention keys and split them.
+ qkv_split_dict = split_tensor(state_dict[old_key], new_key)
+ converted_state_dict.update(qkv_split_dict)
+ else:
+ converted_state_dict[new_key] = state_dict[old_key]
+
+ # Embeddings will not have initial dimension
+ pos_embed_key = "model.vision_model.embeddings.position_embedding.weight"
+ converted_state_dict[pos_embed_key] = converted_state_dict[pos_embed_key].squeeze(0)
+
+ return converted_state_dict
+
+
+def ensure_model_downloaded(
+ repo_id: str | None = None, revision: str | None = None, local_dir: str | None = None
+) -> str:
+ """
+ Ensures model files are downloaded locally, downloads them if not.
+ Returns path to local files.
+
+ Args:
+ repo_id: The Hugging Face model repo ID (required if local_dir not provided)
+ revision: Optional git revision to use
+ local_dir: Optional local directory path where model files should be stored/found
+ """
+ if local_dir is not None:
+ if os.path.exists(local_dir):
+ print(f"Using provided local directory: {local_dir}")
+ else:
+ # Create the local directory if it doesn't exist
+ os.makedirs(local_dir, exist_ok=True)
+ print(f"Created local directory: {local_dir}")
+
+ if repo_id is None:
+ raise ValueError("Either repo_id or local_dir must be provided")
+
+ print(f"Ensuring {repo_id} (revision: {revision or 'latest'}) is downloaded...")
+
+ try:
+ # First try to find files locally
+ download_dir = snapshot_download(repo_id, revision=revision, local_files_only=True, local_dir=local_dir)
+ print(f"Found model files locally at {download_dir}")
+ return download_dir
+ except Exception:
+ # If files not found locally, download them
+ print(f"Downloading model files for {repo_id}...")
+ download_dir = snapshot_download(repo_id, revision=revision, local_files_only=False, local_dir=local_dir)
+ print(f"Downloaded model files to {download_dir}")
+ return download_dir
+
+
+def load_model_state_dict(input_path: str) -> dict:
+ """
+ Load model state dict, handling both single and sharded files.
+ """
+ index_path = os.path.join(input_path, "pytorch_model.bin.index.json")
+ single_file_path = os.path.join(input_path, "pytorch_model.bin")
+
+ # Check if we have a sharded model
+ if os.path.exists(index_path):
+ print("Loading sharded model...")
+ state_dict = {}
+ with open(index_path, "r") as f:
+ index = json.load(f)
+
+ # Get unique shard files and load each one only once
+ unique_shard_files = sorted(set(index["weight_map"].values()))
+ for shard_file in unique_shard_files:
+ print(f"Loading shard {shard_file}...")
+ shard_path = os.path.join(input_path, shard_file)
+ shard_dict = torch.load(shard_path, map_location="cpu")
+ state_dict.update(shard_dict)
+
+ return state_dict
+
+ # Single file model
+ elif os.path.exists(single_file_path):
+ print("Loading single file model...")
+ return torch.load(single_file_path, map_location="cpu")
+
+ else:
+ raise ValueError(f"No model files found in {input_path}")
+
+
+def convert_model(
+ repo_id=None,
+ local_dir=None,
+ text_model_id=None,
+ output_dir=None,
+ output_hub_path=None,
+ revision=None,
+):
+ """Convert and save the model weights, processor, and configuration."""
+ if output_dir is None and output_hub_path is None:
+ raise ValueError("At least one of output_dir or output_hub_path must be specified")
+
+ if repo_id is None and local_dir is None:
+ raise ValueError("Either repo_id or local_dir must be specified")
+
+ # Create output directory if specified
+ if output_dir:
+ os.makedirs(output_dir, exist_ok=True)
+ print(f"Created/verified output directory: {output_dir}")
+
+ torch.set_default_dtype(torch.float16)
+
+ # Download or locate model files
+ input_path = ensure_model_downloaded(repo_id=repo_id, revision=revision, local_dir=local_dir)
+
+ # Load configuration files
+ required_files = ["config.json", "preprocessor_config.json", "special_tokens_map.json", "tokenizer_config.json"]
+
+ missing_files = [f for f in required_files if not os.path.exists(os.path.join(input_path, f))]
+ if missing_files:
+ raise ValueError(
+ f"The following required configuration files are missing from {input_path}: {', '.join(missing_files)}. "
+ "Please ensure you have downloaded all necessary model files."
+ )
+
+ with open(os.path.join(input_path, "config.json"), "r") as f:
+ config_data = json.load(f)
+ with open(os.path.join(input_path, "preprocessor_config.json"), "r") as f:
+ preprocessor_config = json.load(f)
+ with open(os.path.join(input_path, "special_tokens_map.json"), "r") as f:
+ special_tokens_map = json.load(f)
+ with open(os.path.join(input_path, "tokenizer_config.json"), "r") as f:
+ tokenizer_config = json.load(f)
+
+ # Create tokenizer directly from tokenizer.json if it exists
+ tokenizer_json_path = os.path.join(input_path, "tokenizer.json")
+ special_image_tokens = {
+ "image_token": "",
+ "boi_token": "",
+ "eoi_token": "",
+ }
+
+ if os.path.exists(tokenizer_json_path) and not text_model_id:
+ tokenizer = AutoTokenizer.from_pretrained(
+ input_path, # This will load tokenizer.json directly
+ model_max_length=tokenizer_config["model_max_length"],
+ extra_special_tokens=special_image_tokens,
+ )
+ else:
+ # Fallback to creating from text_model_id with special tokens
+ tokenizer = AutoTokenizer.from_pretrained(
+ text_model_id,
+ bos_token=special_tokens_map["bos_token"],
+ eos_token=special_tokens_map["eos_token"],
+ pad_token=special_tokens_map["pad_token"],
+ additional_special_tokens=special_tokens_map["additional_special_tokens"],
+ model_max_length=tokenizer_config["model_max_length"],
+ extra_special_tokens=special_image_tokens,
+ )
+
+ # Create image processor from config
+ image_processor_kwargs = {}
+ for key in ["do_normalize", "image_mean", "image_std", "min_size", "rescale_factor"]:
+ if key in preprocessor_config:
+ image_processor_kwargs[key] = preprocessor_config[key]
+
+ if "image_size" in preprocessor_config:
+ image_processor_kwargs["size"] = {
+ "height": preprocessor_config["image_size"],
+ "width": preprocessor_config["image_size"],
+ }
+
+ image_processor = JanusImageProcessor(**image_processor_kwargs)
+
+ # Create processor with chat template
+ processor = JanusProcessor(
+ image_processor=image_processor,
+ tokenizer=tokenizer,
+ chat_template=CHAT_TEMPLATE,
+ use_default_system_prompt=True,
+ )
+
+ if output_dir:
+ print(f"Saving processor to {output_dir}...")
+ processor.save_pretrained(output_dir)
+ if output_hub_path:
+ print(f"Pushing processor to hub at {output_hub_path}...")
+ processor.push_to_hub(output_hub_path)
+
+ # Create model configurations
+ text_config_kwargs = {}
+ for key in [
+ "vocab_size",
+ "hidden_size",
+ "intermediate_size",
+ "num_hidden_layers",
+ "num_attention_heads",
+ "num_key_value_heads",
+ "hidden_act",
+ "max_position_embeddings",
+ "dtype",
+ ]:
+ if key in config_data["language_config"]:
+ text_config_kwargs[key] = config_data["language_config"][key]
+
+ # Add token IDs from tokenizer
+ text_config_kwargs.update(
+ {
+ "pad_token_id": tokenizer.pad_token_id,
+ "bos_token_id": tokenizer.bos_token_id,
+ "eos_token_id": tokenizer.eos_token_id,
+ }
+ )
+
+ text_config = LlamaConfig(**text_config_kwargs)
+
+ # Create vision config
+ vision_config_kwargs = {}
+ if "image_size" in config_data["vision_config"]["params"]:
+ vision_config_kwargs["image_size"] = config_data["vision_config"]["params"]["image_size"]
+
+ # Add aligner params if present
+ if "aligner_config" in config_data and "params" in config_data["aligner_config"]:
+ if "n_embed" in config_data["aligner_config"]["params"]:
+ vision_config_kwargs["projection_dim"] = config_data["aligner_config"]["params"]["n_embed"]
+ if "depth" in config_data["aligner_config"]["params"]:
+ vision_config_kwargs["depth"] = config_data["aligner_config"]["params"]["depth"]
+
+ vision_config = JanusVisionConfig(**vision_config_kwargs)
+
+ vq_config = JanusVQVAEConfig(
+ embed_dim=config_data["gen_vision_config"]["params"]["n_embed"],
+ num_embeddings=config_data["gen_vision_config"]["params"]["image_token_size"],
+ projection_dim=config_data["gen_aligner_config"]["params"]["n_embed"],
+ depth=config_data["gen_aligner_config"]["params"]["depth"],
+ image_token_embed_dim=config_data["gen_head_config"]["params"]["image_token_embed"],
+ )
+
+ # Create the main config
+ config = JanusConfig(
+ text_config=text_config,
+ vision_config=vision_config,
+ vq_config=vq_config,
+ image_token_id=tokenizer.vocab.get(""),
+ )
+
+ # Save the config
+ if output_dir:
+ config.save_pretrained(output_dir)
+ if output_hub_path:
+ config.push_to_hub(output_hub_path)
+
+ # Initialize model with empty weights
+ print("Creating empty model...")
+ with torch.device("meta"):
+ model = JanusForConditionalGeneration(config)
+
+ model.generation_config._from_model_config = False
+ model.generation_config.temperature = 1
+ model.generation_config.guidance_scale = 5
+ model.generation_config.pad_token_id = tokenizer.vocab.get("<\uff5c\u2581pad\u2581\uff5c>")
+ if not hasattr(model.generation_config, "generation_kwargs"):
+ model.generation_config.generation_kwargs = {}
+ model.generation_config.generation_kwargs["boi_token_id"] = tokenizer.vocab.get("")
+
+ # Load and convert state dict
+ print("Loading state dict...")
+ state_dict = load_model_state_dict(input_path)
+ state_dict = convert_state_dict_to_hf(state_dict)
+
+ # Load converted state dict
+ print("Loading converted weights into model...")
+ model.load_state_dict(state_dict, strict=True, assign=True)
+
+ # Tie weights before any device mapping
+ print("Tying weights...")
+ model.tie_weights()
+
+ # Save the model
+ if output_dir:
+ print(f"Saving model to {output_dir}...")
+ model.save_pretrained(output_dir)
+ if output_hub_path:
+ print(f"Pushing model to hub at {output_hub_path}...")
+ model.push_to_hub(output_hub_path)
+
+ del state_dict, model
+ gc.collect()
+
+ # Validate the saved model if saved locally
+ if output_dir:
+ print("Reloading the local model to check if it's saved correctly...")
+ # TODO: warning about weights not being tied is raised here regardless of model.tie_weights() above
+ JanusForConditionalGeneration.from_pretrained(output_dir, device_map="auto")
+ print("Local model reloaded successfully.")
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "--repo_id",
+ help="HuggingFace Hub repo ID for the model",
+ default=None,
+ )
+ parser.add_argument(
+ "--local_dir",
+ help="Local directory containing the model files",
+ default=None,
+ )
+ parser.add_argument(
+ "--revision",
+ help="Specific revision to download from the Hub",
+ default=None,
+ )
+ parser.add_argument(
+ "--output_dir",
+ help="Location to write HF model locally",
+ default=None,
+ )
+ parser.add_argument(
+ "--output_hub_path",
+ help="Repository ID to push model to hub (e.g. 'username/model-name')",
+ default=None,
+ )
+ parser.add_argument(
+ "--text_model_id",
+ help="Hub ID of the text model to get tokenizer from. Optional if tokenizer.json exists in the model directory.",
+ required=False,
+ )
+ args = parser.parse_args()
+
+ if args.output_dir is None and args.output_hub_path is None:
+ raise ValueError("At least one of --output_dir or --output_hub_path must be specified")
+
+ if args.repo_id is None and args.local_dir is None:
+ raise ValueError("Either --repo_id or --local_dir must be specified")
+
+ convert_model(
+ repo_id=args.repo_id,
+ local_dir=args.local_dir,
+ text_model_id=args.text_model_id,
+ output_dir=args.output_dir,
+ output_hub_path=args.output_hub_path,
+ revision=args.revision,
+ )
+
+
+if __name__ == "__main__":
+ main()
diff --git a/third_party/transformers/src/transformers/models/janus/image_processing_janus.py b/third_party/transformers/src/transformers/models/janus/image_processing_janus.py
new file mode 100644
index 0000000000000000000000000000000000000000..f698dd61cbdede537a410610868ec76624577899
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/janus/image_processing_janus.py
@@ -0,0 +1,228 @@
+# Copyright 2025 Deepseek AI and The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import torch
+import torchvision.transforms.v2.functional as tvF
+
+from ...image_processing_backends import TorchvisionBackend
+from ...image_processing_utils import BatchFeature
+from ...image_transforms import group_images_by_shape, reorder_images
+from ...image_utils import (
+ OPENAI_CLIP_MEAN,
+ OPENAI_CLIP_STD,
+ ImageInput,
+ PILImageResampling,
+ SizeDict,
+)
+from ...processing_utils import ImagesKwargs, Unpack
+from ...utils import (
+ TensorType,
+ auto_docstring,
+)
+
+
+class JanusImageProcessorKwargs(ImagesKwargs, total=False):
+ r"""
+ min_size (`int`, *optional*, defaults to 14):
+ The minimum allowed size for the resized image. Ensures that neither the height nor width
+ falls below this value after resizing.
+ """
+
+ min_size: int
+
+
+@auto_docstring
+class JanusImageProcessor(TorchvisionBackend):
+ resample = PILImageResampling.BICUBIC
+ image_mean = OPENAI_CLIP_MEAN
+ image_std = OPENAI_CLIP_STD
+ size = {"height": 384, "width": 384}
+ min_size = 14
+ do_resize = True
+ do_rescale = True
+ do_normalize = True
+ do_pad = True
+ valid_kwargs = JanusImageProcessorKwargs
+
+ def __init__(self, **kwargs: Unpack[JanusImageProcessorKwargs]):
+ super().__init__(**kwargs)
+ if kwargs.get("image_mean") is None:
+ background_color = (127, 127, 127)
+ else:
+ background_color = tuple(int(x * 255) for x in kwargs.get("image_mean"))
+ self.background_color = tuple(background_color)
+
+ def resize(
+ self,
+ image: "torch.Tensor",
+ size: SizeDict,
+ min_size: int,
+ resample: "PILImageResampling | tvF.InterpolationMode | int | None",
+ antialias: bool = True,
+ **kwargs,
+ ) -> "torch.Tensor":
+ if size.height is None or size.width is None or size.height != size.width:
+ raise ValueError(
+ f"Output height and width must be the same. Got height={size['height']} and width={size['width']}"
+ )
+ size = size.height
+
+ height, width = image.shape[-2:]
+ max_size = max(height, width)
+
+ delta = size / max_size
+ # Largest side becomes `size` and the other side is scaled according to the aspect ratio.
+ output_size_nonpadded = SizeDict(
+ height=max(round(height * delta), min_size),
+ width=max(round(width * delta), min_size),
+ )
+
+ return super().resize(image, size=output_size_nonpadded, resample=resample, antialias=antialias)
+
+ def pad_to_square(
+ self,
+ images: "torch.Tensor",
+ background_color: int | tuple[int, int, int] = 0,
+ ) -> "torch.Tensor":
+ """
+ Pads an image to a square based on the longest edge.
+
+ Args:
+ images (`torch.Tensor`):
+ The images to pad.
+ background_color (`int` or `tuple[int, int, int]`, *optional*, defaults to 0):
+ The color to use for the padding. Can be an integer for single channel or a
+ tuple of integers representing for multi-channel images. If passed as integer
+ in multi-channel mode, it will default to `0` in subsequent channels.
+
+ Returns:
+ `torch.Tensor`: The padded images.
+ """
+ height, width = images.shape[-2:]
+ num_channels = images.shape[1]
+ batch_size = images.shape[0]
+
+ if height == width:
+ return images
+
+ max_dim = max(height, width)
+
+ # Ensure background_color is the correct shape
+ if isinstance(background_color, int):
+ background_color = [background_color]
+ elif len(background_color) != num_channels:
+ raise ValueError(
+ f"background_color must have no more than {num_channels} elements to match the number of channels"
+ )
+
+ padded_images = torch.zeros(
+ (batch_size, num_channels, max_dim, max_dim), dtype=images.dtype, device=images.device
+ )
+ for i, color in enumerate(background_color):
+ padded_images[:, i, :, :] = color
+ if width > height:
+ start = (max_dim - height) // 2
+ padded_images[:, :, start : start + height, :] = images
+ else:
+ start = (max_dim - width) // 2
+ padded_images[:, :, :, start : start + width] = images
+
+ return padded_images
+
+ def _preprocess(
+ self,
+ images: list["torch.Tensor"],
+ do_resize: bool,
+ size: SizeDict,
+ min_size: int,
+ resample: "PILImageResampling | tvF.InterpolationMode | int | None",
+ do_rescale: bool,
+ rescale_factor: float,
+ do_normalize: bool,
+ image_mean: float | list[float] | None,
+ image_std: float | list[float] | None,
+ disable_grouping: bool | None,
+ return_tensors: str | TensorType | None,
+ do_pad: bool = True,
+ **kwargs,
+ ) -> BatchFeature:
+ # Group images by size for batched resizing
+ grouped_images, grouped_images_index = group_images_by_shape(images, disable_grouping=disable_grouping)
+ resized_images_grouped = {}
+ for shape, stacked_images in grouped_images.items():
+ if do_resize:
+ stacked_images = self.resize(image=stacked_images, size=size, min_size=min_size, resample=resample)
+ resized_images_grouped[shape] = stacked_images
+ resized_images = reorder_images(resized_images_grouped, grouped_images_index)
+
+ # Group images by size for further processing
+ # Needed in case do_resize is False, or resize returns images with different sizes
+ grouped_images, grouped_images_index = group_images_by_shape(resized_images, disable_grouping=disable_grouping)
+ processed_images_grouped = {}
+ for shape, stacked_images in grouped_images.items():
+ if do_pad:
+ stacked_images = self.pad_to_square(stacked_images, background_color=self.background_color)
+ # Fused rescale and normalize
+ stacked_images = self.rescale_and_normalize(
+ stacked_images, do_rescale, rescale_factor, do_normalize, image_mean, image_std
+ )
+ processed_images_grouped[shape] = stacked_images
+
+ processed_images = reorder_images(processed_images_grouped, grouped_images_index)
+
+ return BatchFeature(data={"pixel_values": processed_images}, tensor_type=return_tensors)
+
+ def postprocess(
+ self,
+ images: ImageInput,
+ do_rescale: bool | None = None,
+ rescale_factor: float | None = None,
+ do_normalize: bool | None = None,
+ image_mean: list[float] | None = None,
+ image_std: list[float] | None = None,
+ return_tensors: str | None = None,
+ ) -> "torch.Tensor":
+ do_rescale = do_rescale if do_rescale is not None else self.do_rescale
+ rescale_factor = 1.0 / self.rescale_factor if rescale_factor is None else rescale_factor
+ do_normalize = do_normalize if do_normalize is not None else self.do_normalize
+ image_mean = image_mean if image_mean is not None else self.image_mean
+ image_std = image_std if image_std is not None else self.image_std
+ image_mean = tuple(-rescale_factor * mean / std for mean, std in zip(image_mean, image_std))
+ image_std = tuple(1 / std for std in image_std)
+
+ images = self.preprocess(
+ images,
+ do_rescale=do_rescale,
+ rescale_factor=rescale_factor,
+ do_normalize=do_normalize,
+ image_mean=image_mean,
+ image_std=image_std,
+ do_resize=False,
+ do_pad=False,
+ return_tensors=return_tensors,
+ ).pixel_values
+ if do_rescale:
+ images = [image.clip(0, 255).to(torch.uint8) for image in images]
+
+ if do_normalize and do_rescale and return_tensors == "PIL.Image.Image":
+ images = [tvF.to_pil_image(image) for image in images]
+
+ return_tensors = return_tensors if return_tensors != "PIL.Image.Image" else None
+ images = torch.stack(images, dim=0) if return_tensors == "pt" else images
+
+ return BatchFeature(data={"pixel_values": images}, tensor_type=return_tensors)
+
+
+__all__ = ["JanusImageProcessor"]
diff --git a/third_party/transformers/src/transformers/models/janus/image_processing_pil_janus.py b/third_party/transformers/src/transformers/models/janus/image_processing_pil_janus.py
new file mode 100644
index 0000000000000000000000000000000000000000..8a1350f3366bb78de9e611bb58c5b3ad99ea478f
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/janus/image_processing_pil_janus.py
@@ -0,0 +1,271 @@
+# Copyright 2025 Deepseek AI and The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""PIL Image processor class for Janus."""
+
+from collections.abc import Iterable
+
+import numpy as np
+import PIL
+
+from ...image_processing_backends import PilBackend
+from ...image_processing_utils import BatchFeature
+from ...image_transforms import resize as np_resize
+from ...image_transforms import to_channel_dimension_format
+from ...image_utils import (
+ OPENAI_CLIP_MEAN,
+ OPENAI_CLIP_STD,
+ ChannelDimension,
+ ImageInput,
+ PILImageResampling,
+ SizeDict,
+ infer_channel_dimension_format,
+ make_flat_list_of_images,
+ to_numpy_array,
+)
+from ...processing_utils import ImagesKwargs, Unpack
+from ...utils import (
+ TensorType,
+ auto_docstring,
+)
+
+
+# Adapted from transformers.models.janus.image_processing_janus.JanusImageProcessorKwargs
+class JanusImageProcessorKwargs(ImagesKwargs, total=False):
+ r"""
+ min_size (`int`, *optional*, defaults to 14):
+ The minimum allowed size for the resized image. Ensures that neither the height nor width
+ falls below this value after resizing.
+ """
+
+ min_size: int
+
+
+@auto_docstring
+class JanusImageProcessorPil(PilBackend):
+ resample = PILImageResampling.BICUBIC
+ image_mean = OPENAI_CLIP_MEAN
+ image_std = OPENAI_CLIP_STD
+ size = {"height": 384, "width": 384}
+ min_size = 14
+ do_resize = True
+ do_rescale = True
+ do_normalize = True
+ do_pad = True
+ valid_kwargs = JanusImageProcessorKwargs
+
+ def __init__(self, **kwargs: Unpack[JanusImageProcessorKwargs]):
+ super().__init__(**kwargs)
+ image_mean = getattr(self, "image_mean", None)
+ if image_mean is None:
+ background_color = (127, 127, 127)
+ else:
+ background_color = tuple(int(x * 255) for x in image_mean)
+ self.background_color = tuple(background_color)
+
+ @auto_docstring
+ def preprocess(self, images: ImageInput, **kwargs: Unpack[JanusImageProcessorKwargs]) -> BatchFeature:
+ return super().preprocess(images, **kwargs)
+
+ def resize(
+ self,
+ image: np.ndarray,
+ size: SizeDict,
+ min_size: int,
+ resample: PILImageResampling | None = None,
+ **kwargs,
+ ) -> np.ndarray:
+ """Resize so largest side becomes size, with min_size floor."""
+ if size.height is None or size.width is None or size.height != size.width:
+ raise ValueError(
+ f"Output height and width must be the same. Got height={size.height} and width={size.width}"
+ )
+ target_size = size.height
+
+ height, width = image.shape[-2:]
+ max_size = max(height, width)
+
+ delta = target_size / max_size
+ new_height = max(round(height * delta), min_size)
+ new_width = max(round(width * delta), min_size)
+
+ return np_resize(
+ image,
+ size=(new_height, new_width),
+ resample=resample or self.resample,
+ data_format=ChannelDimension.FIRST,
+ input_data_format=ChannelDimension.FIRST,
+ )
+
+ def pad_to_square(
+ self,
+ image: np.ndarray,
+ background_color: int | tuple[int, int, int] = 0,
+ ) -> np.ndarray:
+ """Pad an image to a square based on the longest edge."""
+ height, width = image.shape[-2:]
+ num_channels = image.shape[0]
+
+ if height == width:
+ return image
+
+ max_dim = max(height, width)
+
+ if isinstance(background_color, int):
+ background_color = [background_color]
+ elif len(background_color) != num_channels:
+ raise ValueError(
+ f"background_color must have no more than {num_channels} elements to match the number of channels"
+ )
+
+ padded_image = np.zeros((num_channels, max_dim, max_dim), dtype=image.dtype)
+ for i, color in enumerate(background_color):
+ padded_image[i, :, :] = color
+
+ if width > height:
+ start = (max_dim - height) // 2
+ padded_image[:, start : start + height, :] = image
+ else:
+ start = (max_dim - width) // 2
+ padded_image[:, :, start : start + width] = image
+
+ return padded_image
+
+ def _preprocess(
+ self,
+ images: list[np.ndarray],
+ do_resize: bool,
+ size: SizeDict,
+ resample: PILImageResampling | None,
+ do_rescale: bool,
+ rescale_factor: float,
+ do_normalize: bool,
+ image_mean: float | list[float] | None,
+ image_std: float | list[float] | None,
+ min_size: int,
+ return_tensors: str | TensorType | None,
+ do_pad: bool = True,
+ **kwargs,
+ ) -> BatchFeature:
+ processed_images = []
+ for image in images:
+ if do_resize:
+ image = self.resize(image=image, size=size, min_size=min_size, resample=resample)
+ if do_pad:
+ image = self.pad_to_square(image, background_color=self.background_color)
+ if do_rescale:
+ image = self.rescale(image, rescale_factor)
+ if do_normalize:
+ image = self.normalize(image, image_mean, image_std)
+ processed_images.append(image)
+
+ return BatchFeature(data={"pixel_values": processed_images}, tensor_type=return_tensors)
+
+ def postprocess(
+ self,
+ images: ImageInput,
+ do_rescale: bool | None = None,
+ rescale_factor: float | None = None,
+ do_normalize: bool | None = None,
+ image_mean: list[float] | None = None,
+ image_std: list[float] | None = None,
+ input_data_format: str | None = None,
+ return_tensors: str | None = None,
+ ):
+ """Applies post-processing to the decoded image tokens by reversing transformations applied during preprocessing."""
+ do_rescale = do_rescale if do_rescale is not None else self.do_rescale
+ rescale_factor = 1.0 / self.rescale_factor if rescale_factor is None else rescale_factor
+ do_normalize = do_normalize if do_normalize is not None else self.do_normalize
+ image_mean = image_mean if image_mean is not None else self.image_mean
+ image_std = image_std if image_std is not None else self.image_std
+
+ images = make_flat_list_of_images(images) # Ensures input is a list
+
+ if isinstance(images[0], PIL.Image.Image):
+ return images if len(images) > 1 else images[0]
+
+ if input_data_format is None:
+ input_data_format = infer_channel_dimension_format(images[0]) # Determine format dynamically
+
+ pixel_values = []
+
+ for image in images:
+ image = to_numpy_array(image) # Ensure NumPy format
+
+ if do_normalize:
+ image = self.unnormalize(
+ image=image, image_mean=image_mean, image_std=image_std, input_data_format=input_data_format
+ )
+
+ if do_rescale:
+ image = self.rescale(image, scale=rescale_factor, input_data_format=input_data_format)
+ image = image.clip(0, 255).astype(np.uint8)
+
+ if do_normalize and do_rescale and return_tensors == "PIL.Image.Image":
+ image = to_channel_dimension_format(image, ChannelDimension.LAST, input_channel_dim=input_data_format)
+ image = PIL.Image.fromarray(image)
+
+ pixel_values.append(image)
+
+ data = {"pixel_values": pixel_values}
+ return_tensors = return_tensors if return_tensors != "PIL.Image.Image" else None
+
+ return BatchFeature(data=data, tensor_type=return_tensors)
+
+ def unnormalize(
+ self,
+ image: np.ndarray,
+ image_mean: float | Iterable[float],
+ image_std: float | Iterable[float],
+ input_data_format: str | ChannelDimension | None = None,
+ ) -> np.ndarray:
+ """
+ Unnormalizes `image` using the mean and standard deviation specified by `mean` and `std`.
+ image = (image * image_std) + image_mean
+ Args:
+ image (`torch.Tensor` of shape `(batch_size, num_channels, image_size, image_size)` or `(num_channels, image_size, image_size)`):
+ Batch of pixel values to postprocess.
+ image_mean (`float` or `Iterable[float]`):
+ The mean to use for unnormalization.
+ image_std (`float` or `Iterable[float]`):
+ The standard deviation to use for unnormalization.
+ input_data_format (`ChannelDimension` or `str`, *optional*):
+ The channel dimension format for the input image. If unset, the channel dimension format is inferred
+ from the input image. Can be one of:
+ - `"channels_first"` or `ChannelDimension.FIRST`: image in (num_channels, height, width) format.
+ - `"channels_last"` or `ChannelDimension.LAST`: image in (height, width, num_channels) format.
+ - `"none"` or `ChannelDimension.NONE`: image in (height, width) format.
+ """
+ num_channels = 3
+
+ if isinstance(image_mean, Iterable):
+ if len(image_mean) != num_channels:
+ raise ValueError(f"mean must have {num_channels} elements if it is an iterable, got {len(image_mean)}")
+ else:
+ image_mean = [image_mean] * num_channels
+
+ if isinstance(image_std, Iterable):
+ if len(image_std) != num_channels:
+ raise ValueError(f"std must have {num_channels} elements if it is an iterable, got {len(image_std)}")
+ else:
+ image_std = [image_std] * num_channels
+
+ rev_image_mean = tuple(-mean / std for mean, std in zip(image_mean, image_std))
+ rev_image_std = tuple(1 / std for std in image_std)
+ image = self.normalize(
+ image=image, mean=rev_image_mean, std=rev_image_std, input_data_format=input_data_format
+ )
+ return image
+
+
+__all__ = ["JanusImageProcessorPil"]
diff --git a/third_party/transformers/src/transformers/models/janus/modeling_janus.py b/third_party/transformers/src/transformers/models/janus/modeling_janus.py
new file mode 100644
index 0000000000000000000000000000000000000000..ff2292a9153eaa42c563f9e8e74b4af1259e0243
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/janus/modeling_janus.py
@@ -0,0 +1,1392 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/janus/modular_janus.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_janus.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2025 Deepseek AI and The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from collections.abc import Callable
+from dataclasses import dataclass
+
+import torch
+import torch.nn.functional as F
+from torch import nn
+
+from ... import initialization as init
+from ...activations import ACT2FN
+from ...cache_utils import Cache
+from ...generation import ClassifierFreeGuidanceLogitsProcessor, GenerationMixin, GenerationMode, LogitsProcessorList
+from ...generation.utils import GenerateDecoderOnlyOutput
+from ...modeling_layers import GradientCheckpointingLayer
+from ...modeling_outputs import BaseModelOutput, BaseModelOutputWithPooling, ModelOutput
+from ...modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
+from ...processing_utils import Unpack
+from ...utils import TransformersKwargs, auto_docstring, can_return_tuple, logging, torch_compilable_check, torch_int
+from ...utils.generic import merge_with_config_defaults
+from ...utils.output_capturing import capture_outputs
+from ..auto import AutoModel
+from .configuration_janus import JanusConfig, JanusVisionConfig, JanusVQVAEConfig
+
+
+logger = logging.get_logger(__name__)
+
+
+@auto_docstring
+class JanusPreTrainedModel(PreTrainedModel):
+ config: JanusConfig
+ base_model_prefix = "model"
+ input_modalities = ("image", "text")
+ supports_gradient_checkpointing = True
+ _no_split_modules = ["LlamaDecoderLayer", "JanusVisionEncoderLayer"]
+ _skip_keys_device_placement = ["past_key_values", "causal_mask"]
+ _supports_flash_attn = True
+ _supports_sdpa = True
+
+ _can_compile_fullgraph = True
+
+ def _init_weights(self, module):
+ super()._init_weights(module)
+ if isinstance(module, JanusVisionEmbeddings):
+ init.copy_(module.position_ids, torch.arange(module.position_ids.shape[-1]).expand((1, -1)))
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ Base class for Janus VQ-VAE mode model outputs.
+ """
+)
+class JanusVQVAEOutput(ModelOutput):
+ r"""
+ decoded_pixel_values (`torch.FloatTensor` of shape `(batch_size, num_channels, image_size, image_size)`):
+ Reconstructed pixel values after encoding and decoding the input.
+ embedding_loss (`torch.FloatTensor`):
+ Embedding loss.
+ """
+
+ decoded_pixel_values: torch.FloatTensor | None = None
+ embedding_loss: torch.FloatTensor | None = None
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ Base class for Janus model's outputs that may also contain a past key/values (to speed up sequential decoding).
+ """
+)
+class JanusBaseModelOutputWithPast(ModelOutput):
+ r"""
+ last_hidden_state (`torch.FloatTensor` of shape `(batch_size, sequence_length, hidden_size)`):
+ Sequence of hidden-states at the output of the last layer of the model.
+
+ If `past_key_values` is used only the last hidden-state of the sequences of shape `(batch_size, 1,
+ hidden_size)` is output.
+ past_key_values (`Cache`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
+ It is a [`~cache_utils.Cache`] instance. For more details, see our [kv cache guide](https://huggingface.co/docs/transformers/en/kv_cache).
+
+ Contains pre-computed hidden-states (key and values in the self-attention blocks and optionally if
+ `config.is_encoder_decoder=True` in the cross-attention blocks) that can be used (see `past_key_values`
+ input) to speed up sequential decoding.
+ image_hidden_states (`tuple(torch.FloatTensor)`, *optional*):
+ Tuple of `torch.FloatTensor` (one for the output of the image embeddings, `(batch_size, num_images,
+ sequence_length, hidden_size)`.
+
+ image_hidden_states of the model produced by the vision encoder, and optionally by the perceiver
+ """
+
+ last_hidden_state: torch.FloatTensor | None = None
+ past_key_values: Cache | None = None
+ hidden_states: tuple[torch.FloatTensor] | None = None
+ attentions: tuple[torch.FloatTensor] | None = None
+ image_hidden_states: tuple[torch.FloatTensor] | None = None
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ Base class for Janus causal language model (or autoregressive) outputs.
+ """
+)
+class JanusCausalLMOutputWithPast(ModelOutput):
+ r"""
+ loss (`torch.FloatTensor` of shape `(1,)`, *optional*, returned when `labels` is provided):
+ Language modeling loss (for next-token prediction).
+ logits (`torch.FloatTensor` of shape `(batch_size, sequence_length, config.vocab_size)`):
+ Prediction scores of the language modeling head (scores for each vocabulary token before SoftMax).
+ past_key_values (`Cache`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
+ It is a [`~cache_utils.Cache`] instance. For more details, see our [kv cache guide](https://huggingface.co/docs/transformers/en/kv_cache).
+
+ Contains pre-computed hidden-states (key and values in the self-attention blocks) that can be used (see
+ `past_key_values` input) to speed up sequential decoding.
+ image_hidden_states (`tuple(torch.FloatTensor)`, *optional*):
+ Tuple of `torch.FloatTensor` (one for the output of the image embeddings, `(batch_size, num_images,
+ sequence_length, hidden_size)`.
+
+ image_hidden_states of the model produced by the vision encoder, and optionally by the perceiver
+ """
+
+ loss: torch.FloatTensor | None = None
+ logits: torch.FloatTensor | None = None
+ past_key_values: Cache | None = None
+ hidden_states: tuple[torch.FloatTensor] | None = None
+ attentions: tuple[torch.FloatTensor] | None = None
+ image_hidden_states: tuple[torch.FloatTensor] | None = None
+
+
+class JanusVisionEmbeddings(nn.Module):
+ def __init__(self, config: JanusVisionConfig):
+ super().__init__()
+ self.config = config
+ self.embed_dim = config.hidden_size
+ self.image_size = config.image_size
+ self.patch_size = config.patch_size
+
+ self.patch_embedding = nn.Conv2d(
+ in_channels=config.num_channels,
+ out_channels=self.embed_dim,
+ kernel_size=self.patch_size,
+ stride=self.patch_size,
+ padding="valid",
+ )
+
+ self.num_patches = (self.image_size // self.patch_size) ** 2
+ self.num_positions = self.num_patches
+ self.position_embedding = nn.Embedding(self.num_positions, self.embed_dim)
+ self.register_buffer("position_ids", torch.arange(self.num_positions).expand((1, -1)), persistent=False)
+
+ def interpolate_pos_encoding(self, embeddings: torch.Tensor, height: int, width: int) -> torch.Tensor:
+ """
+ This method allows to interpolate the pre-trained position encodings, to be able to use the model on higher resolution
+ images. This method is also adapted to support torch.jit tracing and no class embeddings.
+
+ Adapted from:
+ - https://github.com/facebookresearch/dino/blob/de9ee3df6cf39fac952ab558447af1fa1365362a/vision_transformer.py#L174-L194, and
+ - https://github.com/facebookresearch/dinov2/blob/e1277af2ba9496fbadf7aec6eba56e8d882d1e35/dinov2/models/vision_transformer.py#L179-L211
+ """
+
+ num_patches = embeddings.shape[1]
+ num_positions = self.position_embedding.weight.shape[0]
+
+ # always interpolate when tracing to ensure the exported model works for dynamic input shapes
+ if not torch.jit.is_tracing() and num_patches == num_positions and height == width:
+ return self.position_embedding(self.position_ids)
+
+ patch_pos_embed = self.position_embedding.weight.unsqueeze(0)
+
+ dim = embeddings.shape[-1]
+
+ new_height = height // self.patch_size
+ new_width = width // self.patch_size
+
+ sqrt_num_positions = torch_int(num_positions**0.5)
+ patch_pos_embed = patch_pos_embed.reshape(1, sqrt_num_positions, sqrt_num_positions, dim)
+ patch_pos_embed = patch_pos_embed.permute(0, 3, 1, 2)
+
+ patch_pos_embed = nn.functional.interpolate(
+ patch_pos_embed,
+ size=(new_height, new_width),
+ mode="bicubic",
+ align_corners=False,
+ )
+
+ patch_pos_embed = patch_pos_embed.permute(0, 2, 3, 1).view(1, -1, dim)
+ return patch_pos_embed
+
+ def forward(self, pixel_values: torch.Tensor, interpolate_pos_encoding: bool = False) -> torch.Tensor:
+ _, _, height, width = pixel_values.shape
+ target_dtype = self.patch_embedding.weight.dtype
+ patch_embeds = self.patch_embedding(pixel_values.to(dtype=target_dtype)) # shape = [*, width, grid, grid]
+ embeddings = patch_embeds.flatten(2).transpose(1, 2)
+
+ if interpolate_pos_encoding:
+ pos_embeds = self.interpolate_pos_encoding(embeddings, height, width)
+ else:
+ pos_embeds = self.position_embedding(self.position_ids)
+
+ embeddings = embeddings + pos_embeds
+
+ return embeddings
+
+
+def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
+ """
+ This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
+ num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
+ """
+ batch, num_key_value_heads, slen, head_dim = hidden_states.shape
+ if n_rep == 1:
+ return hidden_states
+ hidden_states = hidden_states[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim)
+ return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
+
+
+def eager_attention_forward(
+ module: nn.Module,
+ query: torch.Tensor,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ attention_mask: torch.Tensor | None,
+ scaling: float,
+ dropout: float = 0.0,
+ **kwargs: Unpack[TransformersKwargs],
+):
+ key_states = repeat_kv(key, module.num_key_value_groups)
+ value_states = repeat_kv(value, module.num_key_value_groups)
+
+ attn_weights = torch.matmul(query, key_states.transpose(2, 3)) * scaling
+ if attention_mask is not None:
+ attn_weights = attn_weights + attention_mask
+
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query.dtype)
+ attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
+ attn_output = torch.matmul(attn_weights, value_states)
+ attn_output = attn_output.transpose(1, 2).contiguous()
+
+ return attn_output, attn_weights
+
+
+class JanusVisionAttention(nn.Module):
+ """Attention Class for Janus Vision Encoder"""
+
+ def __init__(self, config: JanusVisionConfig):
+ super().__init__()
+ self.config = config
+ self.embed_dim = config.hidden_size
+ self.num_heads = config.num_attention_heads
+ self.head_dim = self.embed_dim // self.num_heads
+ if self.head_dim * self.num_heads != self.embed_dim:
+ raise ValueError(
+ f"embed_dim must be divisible by num_heads (got `embed_dim`: {self.embed_dim} and `num_heads`:"
+ f" {self.num_heads})."
+ )
+ self.scale = self.head_dim**-0.5
+ self.attention_dropout = config.attention_dropout
+ proj_dropout = config.projection_dropout
+ qk_norm = config.use_qk_norm
+ self.is_causal = False
+
+ # Janus has no MHA, hence for `eager_attention_forward` call setting `num_key_value_groups` to 1.
+ self.num_key_value_groups = 1
+
+ self.q_proj = nn.Linear(self.embed_dim, self.num_heads * self.head_dim, bias=config.attention_bias)
+ self.k_proj = nn.Linear(self.embed_dim, self.num_heads * self.head_dim, bias=config.attention_bias)
+ self.v_proj = nn.Linear(self.embed_dim, self.num_heads * self.head_dim, bias=config.attention_bias)
+ self.projection_layer = nn.Linear(self.embed_dim, self.embed_dim)
+ self.projection_dropout = nn.Dropout(proj_dropout) if proj_dropout > 0 else nn.Identity()
+
+ self.q_norm = nn.LayerNorm(self.embed_dim) if qk_norm else nn.Identity()
+ self.k_norm = nn.LayerNorm(self.embed_dim) if qk_norm else nn.Identity()
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ attention_mask: torch.Tensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ):
+ batch_size, seq_len, _ = hidden_states.size()
+
+ query_states = self.q_proj(hidden_states)
+ key_states = self.k_proj(hidden_states)
+ value_states = self.v_proj(hidden_states)
+
+ query_states = query_states.reshape(-1, self.num_heads, self.head_dim)
+ query_states = self.q_norm(query_states)
+
+ key_states = key_states.reshape(-1, self.num_heads, self.head_dim)
+ key_states = self.k_norm(key_states)
+
+ query_states = query_states.reshape(batch_size, seq_len, self.num_heads, self.head_dim).transpose(1, 2)
+ key_states = key_states.reshape(batch_size, seq_len, self.num_heads, self.head_dim).transpose(1, 2)
+ value_states = value_states.view(batch_size, seq_len, self.num_heads, self.head_dim).transpose(1, 2)
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ query_states,
+ key_states,
+ value_states,
+ attention_mask,
+ dropout=0.0 if not self.training else self.attention_dropout,
+ scaling=self.scale,
+ is_causal=self.is_causal,
+ **kwargs,
+ )
+ attn_output = attn_output.reshape(batch_size, seq_len, self.embed_dim)
+
+ output = self.projection_layer(attn_output)
+ output = self.projection_dropout(output)
+ return output, attn_weights
+
+
+class JanusVisionMLP(nn.Module):
+ def __init__(self, config: JanusVisionConfig):
+ super().__init__()
+ self.config = config
+ self.intermediate_size = int(config.hidden_size * config.mlp_ratio)
+ self.activation_fn = ACT2FN[config.hidden_act] # Gelu act
+ self.fc1 = nn.Linear(config.hidden_size, self.intermediate_size)
+ self.fc2 = nn.Linear(self.intermediate_size, config.hidden_size)
+ self.dropout1 = nn.Dropout(config.hidden_dropout_rate)
+ self.dropout2 = nn.Dropout(config.hidden_dropout_rate)
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ hidden_states = self.fc1(hidden_states)
+ hidden_states = self.activation_fn(hidden_states)
+ hidden_states = self.dropout1(hidden_states)
+ hidden_states = self.fc2(hidden_states)
+ hidden_states = self.dropout2(hidden_states)
+ return hidden_states
+
+
+class JanusVisionEncoderLayer(GradientCheckpointingLayer):
+ def __init__(self, config: JanusVisionConfig):
+ super().__init__()
+ self.embed_dim = config.hidden_size
+ self.layer_norm1 = nn.LayerNorm(self.embed_dim, eps=config.layer_norm_eps)
+ self.self_attn = JanusVisionAttention(config)
+ self.layer_norm2 = nn.LayerNorm(self.embed_dim, eps=config.layer_norm_eps)
+ self.mlp = JanusVisionMLP(config)
+ self.config = config
+
+ @auto_docstring
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ attention_mask: torch.Tensor,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> torch.FloatTensor:
+ residual = hidden_states
+
+ hidden_states = self.layer_norm1(hidden_states)
+ hidden_states, _ = self.self_attn(
+ hidden_states=hidden_states,
+ attention_mask=attention_mask,
+ **kwargs,
+ )
+ hidden_states = residual + hidden_states
+
+ residual = hidden_states
+ hidden_states = self.layer_norm2(hidden_states)
+ hidden_states = self.mlp(hidden_states)
+ hidden_states = residual + hidden_states
+
+ return hidden_states
+
+
+class JanusVisionEncoder(nn.Module):
+ """
+ Transformer encoder consisting of `config.num_hidden_layers` self attention layers. Each layer is a
+ [`JanusVisionEncoderLayer`].
+
+ Args:
+ config: JanusVisionConfig
+ """
+
+ def __init__(self, config: JanusVisionConfig):
+ super().__init__()
+ self.config = config
+ self.layers = nn.ModuleList([JanusVisionEncoderLayer(config) for _ in range(config.num_hidden_layers)])
+ self.gradient_checkpointing = False
+
+ # Ignore copy
+ @auto_docstring
+ def forward(
+ self,
+ inputs_embeds,
+ attention_mask: torch.Tensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> BaseModelOutput:
+ hidden_states = inputs_embeds
+ for encoder_layer in self.layers:
+ hidden_states = encoder_layer(
+ hidden_states,
+ attention_mask,
+ **kwargs,
+ )
+
+ return BaseModelOutput(last_hidden_state=hidden_states)
+
+
+@auto_docstring
+class JanusVisionModel(JanusPreTrainedModel):
+ main_input_name = "pixel_values"
+ input_modalities = ("image",)
+ config: JanusVisionConfig
+ _can_record_outputs = {
+ "hidden_states": JanusVisionEncoderLayer,
+ "attentions": JanusVisionAttention,
+ }
+
+ def __init__(self, config: JanusVisionConfig):
+ super().__init__(config)
+ self.config = config
+ embed_dim = config.hidden_size
+
+ self.embeddings = JanusVisionEmbeddings(config)
+ self.encoder = JanusVisionEncoder(config)
+ self.post_layernorm = nn.LayerNorm(embed_dim, eps=config.layer_norm_eps)
+
+ self.post_init()
+
+ @merge_with_config_defaults
+ @capture_outputs(tie_last_hidden_states=False)
+ @auto_docstring
+ def forward(
+ self,
+ pixel_values: torch.FloatTensor | None = None,
+ interpolate_pos_encoding: bool = False,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | BaseModelOutputWithPooling:
+ if pixel_values is None:
+ raise ValueError("You have to specify pixel_values")
+
+ hidden_states = self.embeddings(pixel_values, interpolate_pos_encoding=interpolate_pos_encoding)
+
+ encoder_outputs: BaseModelOutput = self.encoder(
+ inputs_embeds=hidden_states,
+ **kwargs,
+ )
+
+ last_hidden_state = encoder_outputs.last_hidden_state
+ last_hidden_state = self.post_layernorm(last_hidden_state)
+
+ pooled_output = last_hidden_state[:, 0, :]
+ pooled_output = self.post_layernorm(pooled_output)
+
+ return BaseModelOutputWithPooling(
+ last_hidden_state=last_hidden_state,
+ pooler_output=pooled_output,
+ )
+
+ def get_input_embeddings(self):
+ return self.embeddings
+
+
+class JanusVisionAlignerMLP(nn.Module):
+ def __init__(self, config: JanusVisionConfig):
+ super().__init__()
+
+ self.fc1 = nn.Linear(config.hidden_size, config.projection_dim)
+ self.hidden_layers = nn.ModuleList(
+ [nn.Linear(config.projection_dim, config.projection_dim) for _ in range(1, config.depth)]
+ )
+ self.activation_fn = ACT2FN[config.hidden_act]
+
+ def forward(self, hidden_states):
+ hidden_states = self.fc1(hidden_states)
+ for layer in self.hidden_layers:
+ hidden_states = self.activation_fn(hidden_states)
+ hidden_states = layer(hidden_states)
+ return hidden_states
+
+
+class JanusVQVAEVectorQuantizer(nn.Module):
+ """
+ A module for vector quantization using learned embedding vectors.
+
+ This module implements the quantization process similar to te one described in
+ the VQ-VAE (Vector Quantized Variational AutoEncoder) paper. It quantizes continuous
+ input vectors into discrete codebook vectors, which are learned during training.
+ Current implementation improves over previous ones by avoiding costly matrix multiplications
+ and allowing for post-hoc remapping of indices.
+ """
+
+ def __init__(self, config: JanusVQVAEConfig):
+ super().__init__()
+ self.num_embeddings = config.num_embeddings
+ self.embedding_dim = config.embed_dim
+ self.beta = getattr(config, "beta", 0.25)
+
+ self.embedding = nn.Embedding(self.num_embeddings, self.embedding_dim)
+ self.quant_state_dims = [config.num_patches] * 2
+
+ def forward(self, hidden_state: torch.Tensor):
+ hidden_state = hidden_state.permute(0, 2, 3, 1).contiguous()
+ hidden_state_flattened = hidden_state.view(-1, self.embedding_dim)
+
+ # distances from z to embeddings e_j (z - e)^2 = z^2 + e^2 - 2 e * z
+ distances = (
+ torch.sum(hidden_state_flattened**2, dim=1, keepdim=True)
+ + torch.sum(self.embedding.weight**2, dim=1)
+ - 2 * torch.einsum("bd,dn->bn", hidden_state_flattened, self.embedding.weight.transpose(0, 1))
+ )
+
+ min_encoding_indices = torch.argmin(distances, dim=1)
+ hidden_state_quant = self.embedding(min_encoding_indices).view(hidden_state.shape)
+
+ # compute loss for embedding
+ loss = torch.mean((hidden_state_quant.detach() - hidden_state) ** 2) + self.beta * torch.mean(
+ (hidden_state_quant - hidden_state.detach()) ** 2
+ )
+
+ # preserve gradients
+ hidden_state_quant = hidden_state + (hidden_state_quant - hidden_state).detach()
+
+ # reshape back to match original input shape
+ hidden_state_quant = hidden_state_quant.permute(0, 3, 1, 2).contiguous()
+
+ return hidden_state_quant, loss, min_encoding_indices
+
+ def get_codebook_entry(self, image_tokens: torch.LongTensor) -> torch.FloatTensor:
+ batch_size = image_tokens.shape[0]
+ emb_dim: int = self.embedding.weight.shape[-1]
+
+ # get quantized latent vectors
+ hidden_state_quant = self.embedding(image_tokens)
+ # l2 normalization on the last dimension
+ hidden_state_quant = F.normalize(hidden_state_quant, p=2, dim=-1)
+
+ # reshape back to match original input shape
+ hidden_state_quant = hidden_state_quant.view((batch_size, *self.quant_state_dims, emb_dim))
+ hidden_state_quant = hidden_state_quant.permute(0, 3, 1, 2).contiguous()
+
+ return hidden_state_quant
+
+
+class JanusVQVAEResnetBlock(nn.Module):
+ def __init__(
+ self,
+ config,
+ in_channels,
+ out_channels=None,
+ conv_shortcut=False,
+ ):
+ super().__init__()
+ self.in_channels = in_channels
+ self.out_channels = in_channels if out_channels is None else out_channels
+ self.use_conv_shortcut = conv_shortcut
+
+ self.norm1 = torch.nn.GroupNorm(num_groups=32, num_channels=in_channels, eps=1e-6, affine=True)
+ self.conv1 = torch.nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=1, padding=1)
+ self.norm2 = torch.nn.GroupNorm(num_groups=32, num_channels=out_channels, eps=1e-6, affine=True)
+ self.dropout = torch.nn.Dropout(config.dropout)
+ self.conv2 = torch.nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1)
+ if self.in_channels != self.out_channels:
+ if self.use_conv_shortcut:
+ self.conv_shortcut = torch.nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=1, padding=1)
+ else:
+ self.nin_shortcut = torch.nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=1, padding=0)
+
+ def forward(self, hidden_states):
+ residual = hidden_states
+ hidden_states = self.norm1(hidden_states)
+ hidden_states *= torch.sigmoid(hidden_states)
+ hidden_states = self.conv1(hidden_states)
+
+ hidden_states = self.norm2(hidden_states)
+ hidden_states *= torch.sigmoid(hidden_states)
+ hidden_states = self.dropout(hidden_states)
+ hidden_states = self.conv2(hidden_states)
+
+ if self.in_channels != self.out_channels:
+ if self.use_conv_shortcut:
+ residual = self.conv_shortcut(residual)
+ else:
+ residual = self.nin_shortcut(residual)
+
+ return residual + hidden_states
+
+
+class JanusVQVAEAttnBlock(nn.Module):
+ def __init__(self, in_channels):
+ super().__init__()
+ self.in_channels = in_channels
+
+ self.norm = torch.nn.GroupNorm(num_groups=32, num_channels=in_channels, eps=1e-6, affine=True)
+ self.q = torch.nn.Conv2d(in_channels, in_channels, kernel_size=1, stride=1, padding=0)
+ self.k = torch.nn.Conv2d(in_channels, in_channels, kernel_size=1, stride=1, padding=0)
+ self.v = torch.nn.Conv2d(in_channels, in_channels, kernel_size=1, stride=1, padding=0)
+ self.proj_out = torch.nn.Conv2d(in_channels, in_channels, kernel_size=1, stride=1, padding=0)
+
+ def forward(self, hidden_states):
+ residual = hidden_states
+ hidden_states = self.norm(hidden_states)
+ query_states = self.q(hidden_states)
+ key_states = self.k(hidden_states)
+ value_states = self.v(hidden_states)
+
+ # compute attention
+ batch_size, channels, height, width = query_states.shape
+ query_states = query_states.reshape(batch_size, channels, height * width).permute(0, 2, 1)
+ key_states = key_states.reshape(batch_size, channels, height * width)
+ attn_weights = torch.bmm(query_states, key_states)
+ attn_weights = attn_weights * (int(channels) ** (-0.5))
+ attn_weights = F.softmax(attn_weights, dim=2)
+
+ # attend to values
+ value_states = value_states.reshape(batch_size, channels, height * width)
+ attn_weights = attn_weights.permute(0, 2, 1)
+ attn_output = torch.bmm(value_states, attn_weights).reshape(batch_size, channels, height, width)
+
+ attn_output = self.proj_out(attn_output)
+ return residual + attn_output
+
+
+class JanusVQVAEConvDownsample(nn.Module):
+ def __init__(self, in_channels):
+ super().__init__()
+ self.conv = nn.Conv2d(in_channels, in_channels, kernel_size=3, stride=2, padding=0)
+
+ def forward(self, hidden_states):
+ # no asymmetric padding in torch conv, must do it ourselves
+ hidden_states = F.pad(hidden_states, pad=(0, 1, 0, 1), mode="constant", value=0)
+ hidden_states = self.conv(hidden_states)
+ return hidden_states
+
+
+class JanusVQVAEConvUpsample(nn.Module):
+ def __init__(self, in_channels):
+ super().__init__()
+ self.conv = torch.nn.Conv2d(in_channels, in_channels, kernel_size=3, stride=1, padding=1)
+
+ def forward(self, hidden_states):
+ hidden_states = F.interpolate(hidden_states, scale_factor=2.0, mode="nearest")
+ hidden_states = self.conv(hidden_states)
+ return hidden_states
+
+
+class JanusVQVAEMidBlock(nn.Module):
+ def __init__(self, config: JanusVQVAEConfig, channels: int):
+ super().__init__()
+ self.block_1 = JanusVQVAEResnetBlock(
+ config=config,
+ in_channels=channels,
+ out_channels=channels,
+ )
+ self.attn_1 = JanusVQVAEAttnBlock(channels)
+ self.block_2 = JanusVQVAEResnetBlock(
+ config=config,
+ in_channels=channels,
+ out_channels=channels,
+ )
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ hidden_states = self.block_1(hidden_states)
+ hidden_states = self.attn_1(hidden_states)
+ hidden_states = self.block_2(hidden_states)
+ return hidden_states
+
+
+class JanusVQVAEEncoder(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+
+ self.num_resolutions = len(config.channel_multiplier)
+ self.num_res_blocks = config.num_res_blocks
+ base_channels = config.base_channels
+ in_channels = config.in_channels
+ double_latent = config.double_latent
+ latent_channels = config.latent_channels
+ channel_multiplier = config.channel_multiplier
+
+ self.conv_in = torch.nn.Conv2d(in_channels, base_channels, kernel_size=3, stride=1, padding=1)
+
+ in_channel_multiplier = (1,) + tuple(channel_multiplier)
+ self.in_channel_multiplier = in_channel_multiplier
+ self.down = nn.ModuleList()
+ for i_level in range(self.num_resolutions):
+ block = nn.ModuleList()
+ attn = nn.ModuleList()
+ block_in = base_channels * in_channel_multiplier[i_level]
+ block_out = base_channels * channel_multiplier[i_level]
+ for i_block in range(self.num_res_blocks):
+ block.append(
+ JanusVQVAEResnetBlock(
+ config=config,
+ in_channels=block_in,
+ out_channels=block_out,
+ )
+ )
+ block_in = block_out
+ if i_level == self.num_resolutions - 1:
+ attn.append(JanusVQVAEAttnBlock(block_in))
+
+ down = nn.Module()
+ down.block = block
+ down.attn = attn
+ if i_level != self.num_resolutions - 1:
+ down.downsample = JanusVQVAEConvDownsample(block_in)
+ self.down.append(down)
+
+ self.mid = JanusVQVAEMidBlock(config, block_in)
+
+ self.norm_out = torch.nn.GroupNorm(num_groups=32, num_channels=block_in, eps=1e-6, affine=True)
+ self.conv_out = torch.nn.Conv2d(
+ block_in,
+ 2 * latent_channels if double_latent else latent_channels,
+ kernel_size=3,
+ stride=1,
+ padding=1,
+ )
+
+ def forward(self, pixel_values: torch.LongTensor):
+ # downsampling
+ hidden_states = [self.conv_in(pixel_values)]
+ for i_level in range(self.num_resolutions):
+ for i_block in range(self.num_res_blocks):
+ hidden_state = self.down[i_level].block[i_block](
+ hidden_states[-1],
+ )
+ if len(self.down[i_level].attn) > 0:
+ hidden_state = self.down[i_level].attn[i_block](hidden_state)
+ hidden_states.append(hidden_state)
+ if i_level != self.num_resolutions - 1:
+ hidden_states.append(self.down[i_level].downsample(hidden_states[-1]))
+
+ # middle
+ last_hidden_state = hidden_states[-1]
+ last_hidden_state = self.mid(last_hidden_state)
+
+ # end
+ last_hidden_state = self.norm_out(last_hidden_state)
+ last_hidden_state *= torch.sigmoid(last_hidden_state)
+ last_hidden_state = self.conv_out(last_hidden_state)
+ return last_hidden_state
+
+
+class JanusVQVAEDecoder(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+
+ self.num_resolutions = len(config.channel_multiplier)
+ self.num_res_blocks = config.num_res_blocks
+ base_channels = config.base_channels
+ latent_channels = config.latent_channels
+ out_channels = config.out_channels
+
+ # compute in_ch_mult, block_in and curr_res at lowest res
+ block_in = base_channels * config.channel_multiplier[self.num_resolutions - 1]
+
+ # z to block_in
+ self.conv_in = torch.nn.Conv2d(latent_channels, block_in, kernel_size=3, stride=1, padding=1)
+
+ # middle
+ self.mid = JanusVQVAEMidBlock(config, block_in)
+
+ # upsampling
+ self.up = nn.ModuleList()
+ for i_level in reversed(range(self.num_resolutions)):
+ block = nn.ModuleList()
+ attn = nn.ModuleList()
+ block_out = base_channels * config.channel_multiplier[i_level]
+ for i_block in range(self.num_res_blocks + 1):
+ block.append(
+ JanusVQVAEResnetBlock(
+ config=config,
+ in_channels=block_in,
+ out_channels=block_out,
+ )
+ )
+ block_in = block_out
+ if i_level == self.num_resolutions - 1:
+ attn.append(JanusVQVAEAttnBlock(block_in))
+ up = nn.Module()
+ up.block = block
+ up.attn = attn
+ if i_level != 0:
+ up.upsample = JanusVQVAEConvUpsample(block_in)
+ self.up.append(up)
+
+ # end
+ self.norm_out = torch.nn.GroupNorm(num_groups=32, num_channels=block_in, eps=1e-6, affine=True)
+ self.conv_out = torch.nn.Conv2d(block_in, out_channels, kernel_size=3, stride=1, padding=1)
+
+ def forward(self, hidden_state: torch.FloatTensor) -> torch.FloatTensor:
+ hidden_state = self.conv_in(hidden_state)
+
+ # middle
+ hidden_state = self.mid(hidden_state)
+
+ # upsampling
+ for i_level in range(self.num_resolutions):
+ for i_block in range(self.num_res_blocks + 1):
+ hidden_state = self.up[i_level].block[i_block](hidden_state)
+ if len(self.up[i_level].attn) > 0:
+ hidden_state = self.up[i_level].attn[i_block](hidden_state)
+ if i_level != self.num_resolutions - 1:
+ hidden_state = self.up[i_level].upsample(hidden_state)
+
+ hidden_state = self.norm_out(hidden_state)
+ hidden_state *= torch.sigmoid(hidden_state)
+ hidden_state = self.conv_out(hidden_state)
+ return hidden_state
+
+
+@dataclass
+@auto_docstring
+class JanusVQVAEModelOutput(BaseModelOutputWithPooling):
+ r"""
+ quantized_last_hidden_state (`torch.FloatTensor` of shape `(batch_size, num_channels, image_size, image_size)`):
+ Quantized last hidden state from the VQ-VAE model.
+ image_tokens (`torch.FloatTensor` of shape `(batch_size, config.vocab_size`):
+ Indices of the image tokens predicted by the VQ-VAE model.
+ embedding_loss (`torch.FloatTensor`):
+ The embedding loss computed during quantization.
+ """
+
+ quantized_last_hidden_state: torch.FloatTensor | None = None
+ image_tokens: torch.FloatTensor | None = None
+ embedding_loss: torch.FloatTensor | None = None
+
+
+@auto_docstring(
+ custom_intro="""
+ The VQ-VAE model used in Janus for encoding/decoding images into discrete tokens.
+ This model follows the "Make-a-scene: Scene-based text-to-image generation with human priors" paper from
+ [ Oran Gafni, Adam Polyak, Oron Ashual, Shelly Sheynin, Devi Parikh, and Yaniv
+ Taigman](https://huggingface.co/papers/2203.13131).
+ """
+)
+class JanusVQVAE(JanusPreTrainedModel):
+ config: JanusVQVAEConfig
+ _no_split_modules = [
+ "JanusVQVAEAttnBlock",
+ "JanusVQVAEResnetBlock",
+ "JanusVQVAEVectorQuantizer",
+ ]
+ _can_record_outputs = {
+ "hidden_states": JanusVQVAEResnetBlock,
+ "attentions": JanusVQVAEAttnBlock,
+ }
+ main_input_name = "pixel_values"
+
+ def __init__(self, config: JanusVQVAEConfig):
+ super().__init__(config)
+
+ self.encoder = JanusVQVAEEncoder(config)
+ self.quantize = JanusVQVAEVectorQuantizer(config)
+ self.quant_conv = torch.nn.Conv2d(config.latent_channels, config.embed_dim, 1)
+ self.post_quant_conv = torch.nn.Conv2d(config.embed_dim, config.latent_channels, 1)
+ self.eval() # Janus's VQ model is frozen
+ self.decoder = JanusVQVAEDecoder(config)
+ self.gradient_checkpointing = False
+ self.post_init()
+
+ @merge_with_config_defaults
+ @capture_outputs
+ def encode(self, pixel_values: torch.LongTensor, **kwargs: Unpack[TransformersKwargs]) -> JanusVQVAEModelOutput:
+ hidden_states = self.encoder(pixel_values)
+ conv_hidden_states = self.quant_conv(hidden_states)
+ quantized_last_hidden_state, emb_loss, indices = self.quantize(conv_hidden_states)
+ return JanusVQVAEModelOutput(
+ last_hidden_state=hidden_states,
+ quantized_last_hidden_state=quantized_last_hidden_state,
+ image_tokens=indices,
+ embedding_loss=emb_loss,
+ )
+
+ def decode(self, image_tokens: torch.LongTensor) -> torch.FloatTensor:
+ """
+ Decodes quantized token IDs into pixel values.
+ Args:
+ image_tokens (torch.LongTensor): Batch of token IDs.
+ Returns:
+ pixel_values (`torch.FloatTensor` of shape `(batch_size, num_channels, image_size, image_size)`):
+ Pixel values decoded from the token IDs.
+ """
+ if image_tokens.shape[1] != self.quantize.quant_state_dims[0] * self.quantize.quant_state_dims[1]:
+ raise ValueError(
+ f"Expected `image_tokens` to have shape `(batch_size, {self.quantize.quant_state_dims[0] * self.quantize.quant_state_dims[1]})`, "
+ f"but got shape `{image_tokens.shape}`."
+ )
+ codebook_entry = self.quantize.get_codebook_entry(image_tokens)
+ hidden_states = self.post_quant_conv(codebook_entry)
+ pixel_values = self.decoder(hidden_states)
+ return pixel_values
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ pixel_values: torch.FloatTensor,
+ **kwargs,
+ ) -> tuple[torch.FloatTensor, torch.FloatTensor]:
+ batch_size = pixel_values.shape[0]
+ encode_outputs = self.encode(pixel_values, return_dict=True, **kwargs)
+ decoded_pixel_values = self.decode(encode_outputs.image_tokens.view(batch_size, -1))
+
+ return JanusVQVAEOutput(decoded_pixel_values, encode_outputs.embedding_loss)
+
+
+class JanusVQVAEAlignerMLP(nn.Module):
+ def __init__(self, config: JanusVQVAEConfig):
+ super().__init__()
+
+ self.fc1 = nn.Linear(config.embed_dim, config.projection_dim)
+ self.hidden_layers = nn.ModuleList(
+ [nn.Linear(config.projection_dim, config.projection_dim) for _ in range(1, config.num_hidden_layers)]
+ )
+ self.activation_fn = ACT2FN[config.hidden_act]
+
+ def forward(self, hidden_states):
+ hidden_states = self.fc1(hidden_states)
+ for layer in self.hidden_layers:
+ hidden_states = self.activation_fn(hidden_states)
+ hidden_states = layer(hidden_states)
+ return hidden_states
+
+
+class JanusVQVAEHead(nn.Module):
+ """Head used for sampling tokens in image generation, replacing the usual lm head."""
+
+ def __init__(self, config: JanusVQVAEConfig):
+ super().__init__()
+ self.proj_out = nn.Linear(config.image_token_embed_dim, config.projection_dim)
+ self.activation_fn = ACT2FN[config.hidden_act]
+ self.vision_head = nn.Linear(config.projection_dim, config.num_embeddings)
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.tensor:
+ hidden_states = self.proj_out(hidden_states)
+ hidden_states = self.activation_fn(hidden_states)
+ hidden_states = self.vision_head(hidden_states)
+ return hidden_states
+
+
+@auto_docstring(
+ custom_intro="""
+ The Janus model which consists of a siglip vision backbone, a Llama language model and a VQ model.
+ """
+)
+class JanusModel(JanusPreTrainedModel):
+ def __init__(self, config: JanusConfig):
+ super().__init__(config)
+ self.config = config
+ # This is necessary for backward compatibility, see SiglipModel initialization
+ self.vision_model = JanusVisionModel._from_config(config.vision_config)
+ self.aligner = JanusVisionAlignerMLP(self.vision_model.config)
+
+ self.vqmodel = JanusVQVAE._from_config(config.vq_config)
+
+ # Below generation_* modules are used for Image generation.
+ # Embeddings used for image generation, instead of Janus vision embeddings.
+ self.generation_embeddings = nn.Embedding(self.vqmodel.config.num_embeddings, self.vqmodel.config.embed_dim)
+ self.generation_aligner = JanusVQVAEAlignerMLP(self.vqmodel.config)
+ self.generation_head = JanusVQVAEHead(self.vqmodel.config)
+
+ self.language_model = AutoModel.from_config(config=config.text_config)
+
+ self.gradient_checkpointing = False
+ # Initialize weights and apply final processing.
+ self.post_init()
+
+ def get_input_embeddings(self):
+ return self.language_model.get_input_embeddings()
+
+ def set_input_embeddings(self, value):
+ self.language_model.set_input_embeddings(value)
+
+ @can_return_tuple
+ @auto_docstring
+ def get_image_features(
+ self, pixel_values: torch.FloatTensor, **kwargs: Unpack[TransformersKwargs]
+ ) -> tuple | BaseModelOutputWithPooling:
+ vision_outputs = self.vision_model(pixel_values, return_dict=True, **kwargs)
+ vision_outputs.pooler_output = self.aligner(vision_outputs.last_hidden_state)
+
+ return vision_outputs
+
+ def get_placeholder_mask(
+ self, input_ids: torch.LongTensor, inputs_embeds: torch.FloatTensor, image_features: torch.FloatTensor
+ ):
+ """
+ Obtains multimodal placeholder mask from `input_ids` or `inputs_embeds`, and checks that the placeholder token count is
+ equal to the length of multimodal features. If the lengths are different, an error is raised.
+ """
+ if input_ids is None:
+ special_image_mask = inputs_embeds == self.get_input_embeddings()(
+ torch.tensor(self.config.image_token_id, dtype=torch.long, device=inputs_embeds.device)
+ )
+ special_image_mask = special_image_mask.all(-1)
+ else:
+ special_image_mask = input_ids == self.config.image_token_id
+
+ n_image_tokens = special_image_mask.sum()
+ n_image_features = image_features.shape[0] * image_features.shape[1]
+ special_image_mask = special_image_mask.unsqueeze(-1).expand_as(inputs_embeds).to(inputs_embeds.device)
+ torch_compilable_check(
+ inputs_embeds[special_image_mask].numel() == image_features.numel(),
+ f"Image features and image tokens do not match, tokens: {n_image_tokens}, features: {n_image_features}",
+ )
+ return special_image_mask
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ pixel_values: torch.FloatTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ use_cache: bool | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ **kwargs,
+ ) -> JanusBaseModelOutputWithPast:
+ if (input_ids is None) ^ (inputs_embeds is not None):
+ raise ValueError(
+ "You cannot specify both input_ids and inputs_embeds at the same time, and must specify either one"
+ )
+ if inputs_embeds is None:
+ inputs_embeds = self.get_input_embeddings()(input_ids)
+
+ if pixel_values is not None:
+ image_embeds = self.get_image_features(pixel_values, return_dict=True).pooler_output
+ image_features = image_embeds.reshape(-1, inputs_embeds.shape[-1])
+ image_features = image_features.to(inputs_embeds.device, inputs_embeds.dtype)
+ image_attention_mask = self.get_placeholder_mask(
+ input_ids, inputs_embeds=inputs_embeds, image_features=image_features
+ )
+ inputs_embeds = inputs_embeds.masked_scatter(image_attention_mask, image_features)
+
+ lm_output = self.language_model(
+ inputs_embeds=inputs_embeds,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ logits_to_keep=logits_to_keep,
+ **kwargs,
+ )
+
+ return JanusBaseModelOutputWithPast(
+ last_hidden_state=lm_output.last_hidden_state,
+ past_key_values=lm_output.past_key_values,
+ hidden_states=lm_output.hidden_states,
+ attentions=lm_output.attentions,
+ image_hidden_states=image_embeds if pixel_values is not None else None,
+ )
+
+
+class JanusForConditionalGeneration(JanusPreTrainedModel, GenerationMixin):
+ _tied_weights_keys = {"lm_head.weight": "model.language_model.embed_tokens.weight"}
+ output_modalities = ("image", "text")
+ _can_compile_fullgraph = True
+
+ def __init__(self, config: JanusConfig):
+ super().__init__(config)
+ self.config = config
+ self.model = JanusModel(config)
+ self.lm_head = nn.Linear(config.text_config.hidden_size, config.text_config.vocab_size, bias=False)
+
+ # Initialize weights and apply final processing.
+ self.post_init()
+
+ def get_input_embeddings(self):
+ return self.model.language_model.get_input_embeddings()
+
+ def set_input_embeddings(self, value):
+ self.model.language_model.set_input_embeddings(value)
+
+ def prepare_embeddings_for_image_generation(self, inputs: torch.Tensor) -> torch.Tensor:
+ hidden_state = self.model.generation_embeddings(inputs)
+ hidden_state = self.model.generation_aligner(hidden_state)
+ return hidden_state
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ pixel_values: torch.FloatTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> JanusCausalLMOutputWithPast:
+ r"""
+ 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]`.
+ """
+ outputs = self.model(
+ input_ids=input_ids,
+ pixel_values=pixel_values,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ **kwargs,
+ )
+ hidden_states = outputs.last_hidden_state
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(
+ logits=logits, labels=labels, vocab_size=self.config.text_config.vocab_size, **kwargs
+ )
+
+ return JanusCausalLMOutputWithPast(
+ loss=loss,
+ logits=logits,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ image_hidden_states=outputs.image_hidden_states,
+ )
+
+ def prepare_inputs_for_generation(
+ self,
+ input_ids,
+ pixel_values=None,
+ past_key_values=None,
+ attention_mask=None,
+ inputs_embeds=None,
+ logits_to_keep=None,
+ is_first_iteration=False,
+ **kwargs,
+ ):
+ # Overwritten -- extra custom processing
+
+ model_inputs = super().prepare_inputs_for_generation(
+ input_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ attention_mask=attention_mask,
+ logits_to_keep=logits_to_keep,
+ is_first_iteration=is_first_iteration,
+ **kwargs,
+ )
+
+ # Pixel values are used only in the first iteration if available
+ # In subsequent iterations, they are already merged with text and cached
+ # NOTE: first iteration doesn't have to be prefill, it can be the first
+ # iteration with a question and cached system prompt (continue generate from cache)
+ if is_first_iteration or not kwargs.get("use_cache", True):
+ model_inputs["pixel_values"] = pixel_values
+
+ return model_inputs
+
+ def decode_image_tokens(self, image_tokens: torch.Tensor):
+ """
+ Decodes generated image tokens from language model to continuous pixel values
+ with VQGAN module via upsampling.
+ Args:
+ image_tokens (`torch.LongTensor` of shape `(batch_size, num_of_tokens)`):
+ The tensors corresponding to the input images.
+ """
+ decoded_image = self.model.vqmodel.decode(image_tokens)
+ decoded_image = decoded_image.permute(0, 2, 3, 1)
+ return decoded_image
+
+ @torch.no_grad()
+ def generate(
+ self,
+ inputs: torch.Tensor | None = None,
+ attention_mask: torch.LongTensor | None = None,
+ logits_processor: LogitsProcessorList | None = None,
+ **kwargs,
+ ):
+ # 1. Handle generation config and model kwargs
+ # Pop generation_mode first since it's specific to Janus
+ generation_mode = kwargs.pop("generation_mode", "text")
+ generation_config, model_kwargs = self._prepare_generation_config(
+ kwargs.pop("generation_config", None), **kwargs
+ )
+
+ # Default to "text" generation if mode isn't provided
+ if generation_mode == "text":
+ # Set guidance_scale=None to prevent running UnbatchedCFG processor.
+ return super().generate(
+ inputs=inputs,
+ attention_mask=attention_mask,
+ generation_config=generation_config,
+ guidance_scale=None,
+ **model_kwargs,
+ )
+
+ # Validate generation mode
+ if generation_config.get_generation_mode() not in (GenerationMode.SAMPLE, GenerationMode.GREEDY_SEARCH):
+ raise ValueError(
+ "Got incompatible mode for Image Generation, should be one of greedy or sampling. "
+ "Ensure that beam search is de-activated by setting `num_beams=1`."
+ )
+
+ # Validate the configuration and model kwargs
+ generation_config.validate()
+ self._validate_model_kwargs(model_kwargs.copy())
+
+ # 2. Initialize logit processors
+ logits_processor = logits_processor if logits_processor is not None else LogitsProcessorList()
+
+ # Set `use_cache=True` as we will be using input embeds for generation.
+ model_kwargs["use_cache"] = True
+
+ if generation_config.guidance_scale is None:
+ logger.warning("`guidance_scale` is required for CFG but not provided. Setting to default value of 5.")
+ generation_config.guidance_scale = 5
+ model_kwargs["guidance_scale"] = generation_config.guidance_scale
+
+ # 3. Prepare model inputs
+ input_ids, model_input_name, model_kwargs = self._prepare_model_inputs(
+ inputs, generation_config.bos_token_id, model_kwargs
+ )
+ dtype, device = input_ids.dtype, input_ids.device
+
+ if len(input_ids.shape) != 2:
+ raise ValueError(
+ f"Expected input ids of shape (batch_size, seq_len), but got {input_ids.shape}"
+ "Passing `inputs embeds` is not supported currently."
+ )
+
+ # Prepare special tokens which will be used generate internally.
+ kwargs_has_attention_mask = attention_mask is not None
+ self._prepare_special_tokens(generation_config, kwargs_has_attention_mask, device=input_ids.device)
+
+ # 4. Add CFG processor along with user passed logit processor.
+ if generation_config.guidance_scale and generation_config.guidance_scale > 1:
+ logits_processor.append(ClassifierFreeGuidanceLogitsProcessor(generation_config.guidance_scale))
+ generation_config.guidance_scale = None # Reset to prevent processor duplication.
+
+ # 5. Prepare logits processor
+ logits_processor = self._get_logits_processor(
+ generation_config=generation_config,
+ input_ids_seq_length=input_ids.shape[1],
+ encoder_input_ids=input_ids,
+ prefix_allowed_tokens_fn=None,
+ logits_processor=logits_processor,
+ device=device,
+ )
+
+ # 6. Expand inputs for multiple image generations per prompt.
+ input_ids, model_kwargs = self._expand_inputs_for_generation(
+ input_ids=input_ids,
+ attention_mask=attention_mask,
+ expand_size=generation_config.num_return_sequences,
+ **model_kwargs,
+ )
+
+ # 7. Prepare input and model caches
+ num_image_tokens = self.model.vision_model.config.num_image_tokens
+ batch_size, seq_len = input_ids.shape
+
+ input_tokens = input_ids.repeat(2, 1) # Double batch size for conditional/unconditional logits
+ attention_mask = model_kwargs.pop("attention_mask", None)
+ attention_mask = attention_mask.repeat(2, 1)
+ model_kwargs["attention_mask"] = attention_mask
+
+ # Mask all the tokens that are neither BOS nor BOI with pad token in the unconditional logits.
+ mask = (input_tokens[batch_size:, :] != generation_config.bos_token_id) & (
+ input_tokens[batch_size:, :] != generation_config.generation_kwargs["boi_token_id"]
+ )
+ input_tokens[batch_size:, :].masked_fill_(mask, generation_config.pad_token_id)
+
+ inputs_embeds = self.get_input_embeddings()(input_tokens)
+
+ if model_kwargs.get("past_key_values", None) is None:
+ # Prepare cache if not provided.
+ model_kwargs["past_key_values"] = self._prepare_static_cache(
+ cache_implementation=generation_config.cache_implementation or "static",
+ # batch_size should account for both conditional/unconditional input; hence multiplied by 2.
+ batch_size=batch_size * 2,
+ # we should have at least a cache len of seq_len + num_image_tokens.
+ max_cache_len=max(generation_config.max_length, num_image_tokens + seq_len),
+ model_kwargs=model_kwargs,
+ )
+
+ # Placeholder for generated tokens.
+ generated_tokens = torch.zeros((batch_size, num_image_tokens), dtype=dtype, device=device)
+
+ # 8. init attention / hidden states / scores tuples
+ output_attentions = generation_config.output_attentions
+ output_hidden_states = generation_config.output_hidden_states
+ output_scores = generation_config.output_scores
+ output_logits = generation_config.output_logits
+ return_dict_in_generate = generation_config.return_dict_in_generate
+
+ raw_scores = () if (return_dict_in_generate and output_scores) else None
+ raw_logits = () if (return_dict_in_generate and output_logits) else None
+ decoder_hidden_states = () if (return_dict_in_generate and output_hidden_states) else None
+ decoder_attentions = () if (return_dict_in_generate and output_attentions) else None
+
+ for i in range(num_image_tokens):
+ # Set `is_first_iteration=True` to force using `inputs_embeds` instead of `input_ids`.
+ # Without this, `prepare_inputs_for_generation` would use `input_ids` (the full prompt)
+ # instead of our prepared `inputs_embeds` (1 new token).
+ # This causes CUDA error: device-side assert triggered, seen around the call to ` self.self_attn`.
+ # Set this to `True` is also necessary to match the expected output, see the more detailed comment
+ # https://github.com/huggingface/transformers/pull/45044#discussion_r3020805374.
+ model_inputs = self.prepare_inputs_for_generation(
+ inputs_embeds=inputs_embeds, input_ids=input_tokens, is_first_iteration=True, **model_kwargs
+ )
+ if "attention_mask" in model_inputs:
+ model_inputs["attention_mask"] = model_inputs["attention_mask"].to(inputs_embeds.device)
+
+ outputs = self.model.language_model(
+ **model_inputs,
+ output_attentions=output_attentions,
+ output_hidden_states=output_hidden_states,
+ )
+
+ # Update model_kwargs like attention_mask for next generation.
+ model_kwargs = self._update_model_kwargs_for_generation(outputs, model_kwargs)
+ hidden_state = outputs.last_hidden_state[:, -1, :].clone()
+
+ # Generate scores using the generation head (Not using above defined LM Head)
+ scores = self.model.generation_head(hidden_state)
+ next_token_scores = logits_processor(input_ids, scores)
+
+ # Sample next token.
+ if generation_config.do_sample:
+ probs = torch.softmax(next_token_scores, dim=-1)
+ next_token = torch.multinomial(probs, num_samples=1).squeeze(-1)
+ else:
+ next_token = torch.argmax(next_token_scores, dim=-1)
+
+ generated_tokens[:, i] = next_token
+
+ # Prepare embeddings for the next step.
+ next_token = torch.cat([next_token, next_token])
+ next_token = next_token.unsqueeze(-1)
+
+ inputs_embeds = self.prepare_embeddings_for_image_generation(next_token)
+
+ if return_dict_in_generate:
+ if output_scores:
+ raw_scores += (scores,)
+ if output_logits:
+ raw_logits += (hidden_state.float(),)
+ if output_attentions:
+ decoder_attentions += outputs.attentions
+ if output_hidden_states:
+ decoder_hidden_states += outputs.hidden_states
+
+ if return_dict_in_generate:
+ return GenerateDecoderOnlyOutput(
+ sequences=generated_tokens,
+ scores=scores,
+ logits=raw_logits,
+ attentions=decoder_attentions,
+ hidden_states=decoder_hidden_states,
+ past_key_values=outputs.past_key_values,
+ )
+ else:
+ return generated_tokens
+
+
+__all__ = ["JanusPreTrainedModel", "JanusForConditionalGeneration", "JanusModel", "JanusVQVAE", "JanusVisionModel"]
diff --git a/third_party/transformers/src/transformers/models/janus/modular_janus.py b/third_party/transformers/src/transformers/models/janus/modular_janus.py
new file mode 100644
index 0000000000000000000000000000000000000000..9441c3de6ec66360fc2748eceefed8b343ac551e
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/janus/modular_janus.py
@@ -0,0 +1,1165 @@
+# Copyright 2025 Deepseek AI and The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from collections.abc import Callable
+from dataclasses import dataclass
+
+import torch
+import torch.nn.functional as F
+from huggingface_hub.dataclasses import strict
+from torch import nn
+
+from ... import initialization as init
+from ...activations import ACT2FN
+from ...cache_utils import Cache
+from ...configuration_utils import PreTrainedConfig
+from ...generation import ClassifierFreeGuidanceLogitsProcessor, GenerationMixin, GenerationMode, LogitsProcessorList
+from ...generation.utils import GenerateDecoderOnlyOutput
+from ...modeling_outputs import BaseModelOutput, BaseModelOutputWithPooling, ModelOutput
+from ...modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
+from ...processing_utils import Unpack
+from ...utils import (
+ TransformersKwargs,
+ auto_docstring,
+ can_return_tuple,
+ is_vision_available,
+ logging,
+ torch_compilable_check,
+)
+from ..auto import CONFIG_MAPPING, AutoConfig, AutoModel
+from ..blip_2.modeling_blip_2 import Blip2VisionModel
+from ..chameleon.configuration_chameleon import ChameleonVQVAEConfig
+from ..chameleon.modeling_chameleon import (
+ ChameleonVQVAE,
+ ChameleonVQVAEEncoderAttnBlock,
+ ChameleonVQVAEEncoderConvDownsample,
+ ChameleonVQVAEEncoderResnetBlock,
+ ChameleonVQVAEVectorQuantizer,
+)
+from ..idefics.modeling_idefics import IdeficsBaseModelOutputWithPast, IdeficsCausalLMOutputWithPast
+from ..llama.modeling_llama import eager_attention_forward
+from ..siglip.configuration_siglip import SiglipVisionConfig
+from ..siglip.modeling_siglip import SiglipEncoder, SiglipEncoderLayer, SiglipVisionEmbeddings
+
+
+if is_vision_available():
+ pass
+
+logger = logging.get_logger(__name__)
+
+# General docstring
+
+
+@auto_docstring(checkpoint="deepseek-community/Janus-Pro-1B")
+@strict
+class JanusVisionConfig(SiglipVisionConfig):
+ r"""
+ projection_dropout (`float`, *optional*, defaults to 0.0):
+ Dropout probability for the projection layer.
+ num_image_tokens (`int`, *optional*, defaults to 576):
+ Number of image tokens.
+ """
+
+ hidden_size: int = 1024
+ num_hidden_layers: int = 24
+ num_attention_heads: int = 16
+ image_size: int | list[int] | tuple[int, int] = 384
+ hidden_act: str = "gelu"
+ mlp_ratio: float | int = 4.0
+ attention_bias: bool = True
+ hidden_dropout_rate: float | int = 0.0
+ projection_dim: int = 2048
+ projection_dropout: float | int = 0.0
+ use_qk_norm: bool = False
+ initializer_range: float = 0.02
+ depth: int = 2
+ num_image_tokens: int = 576
+ intermediate_size = AttributeError()
+
+
+@auto_docstring(checkpoint="deepseek-community/Janus-Pro-1B")
+@strict
+class JanusVQVAEConfig(ChameleonVQVAEConfig):
+ r"""
+ base_channels (`int`, *optional*, defaults to 128):
+ Base channel count.
+ channel_multiplier (`list[int]`, *optional*, defaults to `[1, 1, 2, 2, 4]`):
+ Channel multipliers for each resolution.
+ num_res_blocks (`int`, *optional*, defaults to 2):
+ Number of residual blocks.
+ num_patches (`int`, *optional*, defaults to 32):
+ Num of patches the input images can be divided into.
+ out_channels (`int`, *optional*, defaults to 3):
+ Number of out channels.
+ image_token_embed_dim (`int`, *optional*, defaults to 2048):
+ Dimension of image embeddings. It should be same as the dimensionality of text embeddings.
+ """
+
+ embed_dim: int = 8
+ num_embeddings: int = 16384
+ double_latent: bool = False
+ latent_channels: int = 256
+ num_patches: int = 32
+ in_channels: int = 3
+ out_channels: int = 3
+ base_channels: int = 128
+ channel_multiplier: list[int] | tuple[int, ...] = (1, 1, 2, 2, 4)
+ num_res_blocks: int = 2
+ dropout: float | int = 0.0
+ initializer_range: float = 0.02
+ projection_dim: int = 2048
+ num_hidden_layers: int = 2
+ hidden_act: str = "gelu"
+ image_token_embed_dim: int = 2048
+
+ resolution = AttributeError()
+ attn_resolutions = AttributeError()
+ attn_type = AttributeError()
+
+
+@auto_docstring(checkpoint="deepseek-community/Janus-Pro-1B")
+@strict
+class JanusConfig(PreTrainedConfig):
+ r"""
+ Example:
+
+ ```python
+ >>> from transformers import JanusForConditionalGeneration, JanusConfig, JanusVisionConfig, JanusVQVAEConfig, LlamaConfig
+
+ >>> # Initializing a Janus vision config
+ >>> vision_config = JanusVisionConfig()
+
+ >>> # Initializing a Llama config
+ >>> text_config = LlamaConfig()
+
+ >>> # Initializing a VQ config
+ >>> vq_config = JanusVQVAEConfig()
+
+ >>> # Initializing a Janus Pro 1B style configuration
+ >>> configuration = JanusConfig(vision_config=vision_config, text_config=text_config, vq_config=vq_config)
+
+ >>> # Initializing a model from the Janus Pro 1B style configuration
+ >>> model = JanusForConditionalGeneration(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "janus"
+ sub_configs = {
+ "text_config": AutoConfig,
+ "vision_config": JanusVisionConfig,
+ "vq_config": JanusVQVAEConfig,
+ }
+
+ text_config: dict | PreTrainedConfig | None = None
+ vision_config: dict | PreTrainedConfig | None = None
+ vq_config: dict | PreTrainedConfig | None = None
+ image_token_id: int = 100581
+
+ def __post_init__(self, **kwargs):
+ if isinstance(self.text_config, dict):
+ self.text_config["model_type"] = self.text_config.get("model_type", "llama")
+ self.text_config = CONFIG_MAPPING[self.text_config["model_type"]](**self.text_config)
+ elif self.text_config is None:
+ logger.info("`text_config` is None. Initializing with default values")
+ self.text_config = CONFIG_MAPPING["llama"]()
+
+ if self.vision_config is None:
+ logger.info("`vision_config` is None. Initializing with default JanusVisionConfig values")
+ self.vision_config = JanusVisionConfig()
+ elif isinstance(self.vision_config, dict):
+ self.vision_config = JanusVisionConfig(**self.vision_config)
+
+ if self.vq_config is None:
+ logger.info("`vq_config` is None. Initializing with default JanusVQVAEConfig values")
+ self.vq_config = JanusVQVAEConfig()
+ elif isinstance(self.vq_config, dict):
+ self.vq_config = JanusVQVAEConfig(**self.vq_config)
+
+ # This dimension is required when decoding discrete image tokens to continuous input.
+ self.vq_config.num_patches = self.vision_config.image_size // self.vision_config.patch_size
+ super().__post_init__(**kwargs)
+
+
+@auto_docstring
+class JanusPreTrainedModel(PreTrainedModel):
+ config: JanusConfig
+ base_model_prefix = "model"
+ input_modalities = ("image", "text")
+ supports_gradient_checkpointing = True
+ _no_split_modules = ["LlamaDecoderLayer", "JanusVisionEncoderLayer"]
+ _skip_keys_device_placement = ["past_key_values", "causal_mask"]
+ _supports_flash_attn = True
+ _supports_sdpa = True
+
+ _can_compile_fullgraph = True
+
+ def _init_weights(self, module):
+ super()._init_weights(module)
+ if isinstance(module, JanusVisionEmbeddings):
+ init.copy_(module.position_ids, torch.arange(module.position_ids.shape[-1]).expand((1, -1)))
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ Base class for Janus VQ-VAE mode model outputs.
+ """
+)
+class JanusVQVAEOutput(ModelOutput):
+ r"""
+ decoded_pixel_values (`torch.FloatTensor` of shape `(batch_size, num_channels, image_size, image_size)`):
+ Reconstructed pixel values after encoding and decoding the input.
+ embedding_loss (`torch.FloatTensor`):
+ Embedding loss.
+ """
+
+ decoded_pixel_values: torch.FloatTensor | None = None
+ embedding_loss: torch.FloatTensor | None = None
+
+
+class JanusBaseModelOutputWithPast(IdeficsBaseModelOutputWithPast):
+ pass
+
+
+class JanusCausalLMOutputWithPast(IdeficsCausalLMOutputWithPast):
+ pass
+
+
+class JanusVisionEmbeddings(SiglipVisionEmbeddings):
+ def forward(self, pixel_values: torch.Tensor, interpolate_pos_encoding: bool = False) -> torch.Tensor:
+ _, _, height, width = pixel_values.shape
+ target_dtype = self.patch_embedding.weight.dtype
+ patch_embeds = self.patch_embedding(pixel_values.to(dtype=target_dtype)) # shape = [*, width, grid, grid]
+ embeddings = patch_embeds.flatten(2).transpose(1, 2)
+
+ if interpolate_pos_encoding:
+ pos_embeds = self.interpolate_pos_encoding(embeddings, height, width)
+ else:
+ pos_embeds = self.position_embedding(self.position_ids)
+
+ embeddings = embeddings + pos_embeds
+
+ return embeddings
+
+
+class JanusVisionAttention(nn.Module):
+ """Attention Class for Janus Vision Encoder"""
+
+ def __init__(self, config: JanusVisionConfig):
+ super().__init__()
+ self.config = config
+ self.embed_dim = config.hidden_size
+ self.num_heads = config.num_attention_heads
+ self.head_dim = self.embed_dim // self.num_heads
+ if self.head_dim * self.num_heads != self.embed_dim:
+ raise ValueError(
+ f"embed_dim must be divisible by num_heads (got `embed_dim`: {self.embed_dim} and `num_heads`:"
+ f" {self.num_heads})."
+ )
+ self.scale = self.head_dim**-0.5
+ self.attention_dropout = config.attention_dropout
+ proj_dropout = config.projection_dropout
+ qk_norm = config.use_qk_norm
+ self.is_causal = False
+
+ # Janus has no MHA, hence for `eager_attention_forward` call setting `num_key_value_groups` to 1.
+ self.num_key_value_groups = 1
+
+ self.q_proj = nn.Linear(self.embed_dim, self.num_heads * self.head_dim, bias=config.attention_bias)
+ self.k_proj = nn.Linear(self.embed_dim, self.num_heads * self.head_dim, bias=config.attention_bias)
+ self.v_proj = nn.Linear(self.embed_dim, self.num_heads * self.head_dim, bias=config.attention_bias)
+ self.projection_layer = nn.Linear(self.embed_dim, self.embed_dim)
+ self.projection_dropout = nn.Dropout(proj_dropout) if proj_dropout > 0 else nn.Identity()
+
+ self.q_norm = nn.LayerNorm(self.embed_dim) if qk_norm else nn.Identity()
+ self.k_norm = nn.LayerNorm(self.embed_dim) if qk_norm else nn.Identity()
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ attention_mask: torch.Tensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ):
+ batch_size, seq_len, _ = hidden_states.size()
+
+ query_states = self.q_proj(hidden_states)
+ key_states = self.k_proj(hidden_states)
+ value_states = self.v_proj(hidden_states)
+
+ query_states = query_states.reshape(-1, self.num_heads, self.head_dim)
+ query_states = self.q_norm(query_states)
+
+ key_states = key_states.reshape(-1, self.num_heads, self.head_dim)
+ key_states = self.k_norm(key_states)
+
+ query_states = query_states.reshape(batch_size, seq_len, self.num_heads, self.head_dim).transpose(1, 2)
+ key_states = key_states.reshape(batch_size, seq_len, self.num_heads, self.head_dim).transpose(1, 2)
+ value_states = value_states.view(batch_size, seq_len, self.num_heads, self.head_dim).transpose(1, 2)
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ query_states,
+ key_states,
+ value_states,
+ attention_mask,
+ dropout=0.0 if not self.training else self.attention_dropout,
+ scaling=self.scale,
+ is_causal=self.is_causal,
+ **kwargs,
+ )
+ attn_output = attn_output.reshape(batch_size, seq_len, self.embed_dim)
+
+ output = self.projection_layer(attn_output)
+ output = self.projection_dropout(output)
+ return output, attn_weights
+
+
+class JanusVisionMLP(nn.Module):
+ def __init__(self, config: JanusVisionConfig):
+ super().__init__()
+ self.config = config
+ self.intermediate_size = int(config.hidden_size * config.mlp_ratio)
+ self.activation_fn = ACT2FN[config.hidden_act] # Gelu act
+ self.fc1 = nn.Linear(config.hidden_size, self.intermediate_size)
+ self.fc2 = nn.Linear(self.intermediate_size, config.hidden_size)
+ self.dropout1 = nn.Dropout(config.hidden_dropout_rate)
+ self.dropout2 = nn.Dropout(config.hidden_dropout_rate)
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ hidden_states = self.fc1(hidden_states)
+ hidden_states = self.activation_fn(hidden_states)
+ hidden_states = self.dropout1(hidden_states)
+ hidden_states = self.fc2(hidden_states)
+ hidden_states = self.dropout2(hidden_states)
+ return hidden_states
+
+
+class JanusVisionEncoderLayer(SiglipEncoderLayer):
+ def __init__(self, config: JanusVisionConfig):
+ super().__init__(config)
+ self.config = config
+ self.embed_dim = config.hidden_size
+ self.self_attn = JanusVisionAttention(config)
+ self.layer_norm1 = nn.LayerNorm(self.embed_dim, eps=config.layer_norm_eps)
+ self.layer_norm2 = nn.LayerNorm(self.embed_dim, eps=config.layer_norm_eps)
+ self.mlp = JanusVisionMLP(config)
+
+
+class JanusVisionEncoder(SiglipEncoder):
+ def __init__(self, config: JanusVisionConfig):
+ super().__init__(config)
+ self.layers = nn.ModuleList([JanusVisionEncoderLayer(config) for _ in range(config.num_hidden_layers)])
+
+
+class JanusVisionModel(Blip2VisionModel):
+ _can_record_outputs = {
+ "hidden_states": JanusVisionEncoderLayer,
+ "attentions": JanusVisionAttention,
+ }
+
+ def __init__(self, config: JanusVisionConfig):
+ super().__init__(config)
+ self.encoder = JanusVisionEncoder(config)
+
+ def forward(
+ self,
+ pixel_values: torch.FloatTensor | None = None,
+ interpolate_pos_encoding: bool = False,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | BaseModelOutputWithPooling:
+ if pixel_values is None:
+ raise ValueError("You have to specify pixel_values")
+
+ hidden_states = self.embeddings(pixel_values, interpolate_pos_encoding=interpolate_pos_encoding)
+
+ encoder_outputs: BaseModelOutput = self.encoder(
+ inputs_embeds=hidden_states,
+ **kwargs,
+ )
+
+ last_hidden_state = encoder_outputs.last_hidden_state
+ last_hidden_state = self.post_layernorm(last_hidden_state)
+
+ pooled_output = last_hidden_state[:, 0, :]
+ pooled_output = self.post_layernorm(pooled_output)
+
+ return BaseModelOutputWithPooling(
+ last_hidden_state=last_hidden_state,
+ pooler_output=pooled_output,
+ )
+
+
+class JanusVisionAlignerMLP(nn.Module):
+ def __init__(self, config: JanusVisionConfig):
+ super().__init__()
+
+ self.fc1 = nn.Linear(config.hidden_size, config.projection_dim)
+ self.hidden_layers = nn.ModuleList(
+ [nn.Linear(config.projection_dim, config.projection_dim) for _ in range(1, config.depth)]
+ )
+ self.activation_fn = ACT2FN[config.hidden_act]
+
+ def forward(self, hidden_states):
+ hidden_states = self.fc1(hidden_states)
+ for layer in self.hidden_layers:
+ hidden_states = self.activation_fn(hidden_states)
+ hidden_states = layer(hidden_states)
+ return hidden_states
+
+
+class JanusVQVAEVectorQuantizer(ChameleonVQVAEVectorQuantizer):
+ def __init__(self, config: JanusVQVAEConfig):
+ super().__init__(config)
+ self.quant_state_dims = [config.num_patches] * 2
+
+ def get_codebook_entry(self, image_tokens: torch.LongTensor) -> torch.FloatTensor:
+ batch_size = image_tokens.shape[0]
+ emb_dim: int = self.embedding.weight.shape[-1]
+
+ # get quantized latent vectors
+ hidden_state_quant = self.embedding(image_tokens)
+ # l2 normalization on the last dimension
+ hidden_state_quant = F.normalize(hidden_state_quant, p=2, dim=-1)
+
+ # reshape back to match original input shape
+ hidden_state_quant = hidden_state_quant.view((batch_size, *self.quant_state_dims, emb_dim))
+ hidden_state_quant = hidden_state_quant.permute(0, 3, 1, 2).contiguous()
+
+ return hidden_state_quant
+
+
+class JanusVQVAEResnetBlock(ChameleonVQVAEEncoderResnetBlock):
+ pass
+
+
+class JanusVQVAEAttnBlock(ChameleonVQVAEEncoderAttnBlock):
+ pass
+
+
+class JanusVQVAEConvDownsample(ChameleonVQVAEEncoderConvDownsample):
+ pass
+
+
+class JanusVQVAEConvUpsample(nn.Module):
+ def __init__(self, in_channels):
+ super().__init__()
+ self.conv = torch.nn.Conv2d(in_channels, in_channels, kernel_size=3, stride=1, padding=1)
+
+ def forward(self, hidden_states):
+ hidden_states = F.interpolate(hidden_states, scale_factor=2.0, mode="nearest")
+ hidden_states = self.conv(hidden_states)
+ return hidden_states
+
+
+class JanusVQVAEMidBlock(nn.Module):
+ def __init__(self, config: JanusVQVAEConfig, channels: int):
+ super().__init__()
+ self.block_1 = JanusVQVAEResnetBlock(
+ config=config,
+ in_channels=channels,
+ out_channels=channels,
+ )
+ self.attn_1 = JanusVQVAEAttnBlock(channels)
+ self.block_2 = JanusVQVAEResnetBlock(
+ config=config,
+ in_channels=channels,
+ out_channels=channels,
+ )
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ hidden_states = self.block_1(hidden_states)
+ hidden_states = self.attn_1(hidden_states)
+ hidden_states = self.block_2(hidden_states)
+ return hidden_states
+
+
+class JanusVQVAEEncoder(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+
+ self.num_resolutions = len(config.channel_multiplier)
+ self.num_res_blocks = config.num_res_blocks
+ base_channels = config.base_channels
+ in_channels = config.in_channels
+ double_latent = config.double_latent
+ latent_channels = config.latent_channels
+ channel_multiplier = config.channel_multiplier
+
+ self.conv_in = torch.nn.Conv2d(in_channels, base_channels, kernel_size=3, stride=1, padding=1)
+
+ in_channel_multiplier = (1,) + tuple(channel_multiplier)
+ self.in_channel_multiplier = in_channel_multiplier
+ self.down = nn.ModuleList()
+ for i_level in range(self.num_resolutions):
+ block = nn.ModuleList()
+ attn = nn.ModuleList()
+ block_in = base_channels * in_channel_multiplier[i_level]
+ block_out = base_channels * channel_multiplier[i_level]
+ for i_block in range(self.num_res_blocks):
+ block.append(
+ JanusVQVAEResnetBlock(
+ config=config,
+ in_channels=block_in,
+ out_channels=block_out,
+ )
+ )
+ block_in = block_out
+ if i_level == self.num_resolutions - 1:
+ attn.append(JanusVQVAEAttnBlock(block_in))
+
+ down = nn.Module()
+ down.block = block
+ down.attn = attn
+ if i_level != self.num_resolutions - 1:
+ down.downsample = JanusVQVAEConvDownsample(block_in)
+ self.down.append(down)
+
+ self.mid = JanusVQVAEMidBlock(config, block_in)
+
+ self.norm_out = torch.nn.GroupNorm(num_groups=32, num_channels=block_in, eps=1e-6, affine=True)
+ self.conv_out = torch.nn.Conv2d(
+ block_in,
+ 2 * latent_channels if double_latent else latent_channels,
+ kernel_size=3,
+ stride=1,
+ padding=1,
+ )
+
+ def forward(self, pixel_values: torch.LongTensor):
+ # downsampling
+ hidden_states = [self.conv_in(pixel_values)]
+ for i_level in range(self.num_resolutions):
+ for i_block in range(self.num_res_blocks):
+ hidden_state = self.down[i_level].block[i_block](
+ hidden_states[-1],
+ )
+ if len(self.down[i_level].attn) > 0:
+ hidden_state = self.down[i_level].attn[i_block](hidden_state)
+ hidden_states.append(hidden_state)
+ if i_level != self.num_resolutions - 1:
+ hidden_states.append(self.down[i_level].downsample(hidden_states[-1]))
+
+ # middle
+ last_hidden_state = hidden_states[-1]
+ last_hidden_state = self.mid(last_hidden_state)
+
+ # end
+ last_hidden_state = self.norm_out(last_hidden_state)
+ last_hidden_state *= torch.sigmoid(last_hidden_state)
+ last_hidden_state = self.conv_out(last_hidden_state)
+ return last_hidden_state
+
+
+class JanusVQVAEDecoder(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+
+ self.num_resolutions = len(config.channel_multiplier)
+ self.num_res_blocks = config.num_res_blocks
+ base_channels = config.base_channels
+ latent_channels = config.latent_channels
+ out_channels = config.out_channels
+
+ # compute in_ch_mult, block_in and curr_res at lowest res
+ block_in = base_channels * config.channel_multiplier[self.num_resolutions - 1]
+
+ # z to block_in
+ self.conv_in = torch.nn.Conv2d(latent_channels, block_in, kernel_size=3, stride=1, padding=1)
+
+ # middle
+ self.mid = JanusVQVAEMidBlock(config, block_in)
+
+ # upsampling
+ self.up = nn.ModuleList()
+ for i_level in reversed(range(self.num_resolutions)):
+ block = nn.ModuleList()
+ attn = nn.ModuleList()
+ block_out = base_channels * config.channel_multiplier[i_level]
+ for i_block in range(self.num_res_blocks + 1):
+ block.append(
+ JanusVQVAEResnetBlock(
+ config=config,
+ in_channels=block_in,
+ out_channels=block_out,
+ )
+ )
+ block_in = block_out
+ if i_level == self.num_resolutions - 1:
+ attn.append(JanusVQVAEAttnBlock(block_in))
+ up = nn.Module()
+ up.block = block
+ up.attn = attn
+ if i_level != 0:
+ up.upsample = JanusVQVAEConvUpsample(block_in)
+ self.up.append(up)
+
+ # end
+ self.norm_out = torch.nn.GroupNorm(num_groups=32, num_channels=block_in, eps=1e-6, affine=True)
+ self.conv_out = torch.nn.Conv2d(block_in, out_channels, kernel_size=3, stride=1, padding=1)
+
+ def forward(self, hidden_state: torch.FloatTensor) -> torch.FloatTensor:
+ hidden_state = self.conv_in(hidden_state)
+
+ # middle
+ hidden_state = self.mid(hidden_state)
+
+ # upsampling
+ for i_level in range(self.num_resolutions):
+ for i_block in range(self.num_res_blocks + 1):
+ hidden_state = self.up[i_level].block[i_block](hidden_state)
+ if len(self.up[i_level].attn) > 0:
+ hidden_state = self.up[i_level].attn[i_block](hidden_state)
+ if i_level != self.num_resolutions - 1:
+ hidden_state = self.up[i_level].upsample(hidden_state)
+
+ hidden_state = self.norm_out(hidden_state)
+ hidden_state *= torch.sigmoid(hidden_state)
+ hidden_state = self.conv_out(hidden_state)
+ return hidden_state
+
+
+class JanusVQVAE(ChameleonVQVAE):
+ _no_split_modules = [
+ "JanusVQVAEAttnBlock",
+ "JanusVQVAEResnetBlock",
+ "JanusVQVAEVectorQuantizer",
+ ]
+ _can_record_outputs = {
+ "hidden_states": JanusVQVAEResnetBlock,
+ "attentions": JanusVQVAEAttnBlock,
+ }
+ main_input_name = "pixel_values"
+
+ def __init__(self, config: JanusVQVAEConfig):
+ super().__init__(config)
+ self.decoder = JanusVQVAEDecoder(config)
+ self.gradient_checkpointing = False
+
+ # Initialize the VQVAE model.
+ self.post_init()
+
+ def decode(self, image_tokens: torch.LongTensor) -> torch.FloatTensor:
+ """
+ Decodes quantized token IDs into pixel values.
+ Args:
+ image_tokens (torch.LongTensor): Batch of token IDs.
+ Returns:
+ pixel_values (`torch.FloatTensor` of shape `(batch_size, num_channels, image_size, image_size)`):
+ Pixel values decoded from the token IDs.
+ """
+ if image_tokens.shape[1] != self.quantize.quant_state_dims[0] * self.quantize.quant_state_dims[1]:
+ raise ValueError(
+ f"Expected `image_tokens` to have shape `(batch_size, {self.quantize.quant_state_dims[0] * self.quantize.quant_state_dims[1]})`, "
+ f"but got shape `{image_tokens.shape}`."
+ )
+ codebook_entry = self.quantize.get_codebook_entry(image_tokens)
+ hidden_states = self.post_quant_conv(codebook_entry)
+ pixel_values = self.decoder(hidden_states)
+ return pixel_values
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ pixel_values: torch.FloatTensor,
+ **kwargs,
+ ) -> tuple[torch.FloatTensor, torch.FloatTensor]:
+ batch_size = pixel_values.shape[0]
+ encode_outputs = self.encode(pixel_values, return_dict=True, **kwargs)
+ decoded_pixel_values = self.decode(encode_outputs.image_tokens.view(batch_size, -1))
+
+ return JanusVQVAEOutput(decoded_pixel_values, encode_outputs.embedding_loss)
+
+
+class JanusVQVAEAlignerMLP(nn.Module):
+ def __init__(self, config: JanusVQVAEConfig):
+ super().__init__()
+
+ self.fc1 = nn.Linear(config.embed_dim, config.projection_dim)
+ self.hidden_layers = nn.ModuleList(
+ [nn.Linear(config.projection_dim, config.projection_dim) for _ in range(1, config.num_hidden_layers)]
+ )
+ self.activation_fn = ACT2FN[config.hidden_act]
+
+ def forward(self, hidden_states):
+ hidden_states = self.fc1(hidden_states)
+ for layer in self.hidden_layers:
+ hidden_states = self.activation_fn(hidden_states)
+ hidden_states = layer(hidden_states)
+ return hidden_states
+
+
+class JanusVQVAEHead(nn.Module):
+ """Head used for sampling tokens in image generation, replacing the usual lm head."""
+
+ def __init__(self, config: JanusVQVAEConfig):
+ super().__init__()
+ self.proj_out = nn.Linear(config.image_token_embed_dim, config.projection_dim)
+ self.activation_fn = ACT2FN[config.hidden_act]
+ self.vision_head = nn.Linear(config.projection_dim, config.num_embeddings)
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.tensor:
+ hidden_states = self.proj_out(hidden_states)
+ hidden_states = self.activation_fn(hidden_states)
+ hidden_states = self.vision_head(hidden_states)
+ return hidden_states
+
+
+@auto_docstring(
+ custom_intro="""
+ The Janus model which consists of a siglip vision backbone, a Llama language model and a VQ model.
+ """
+)
+class JanusModel(JanusPreTrainedModel):
+ def __init__(self, config: JanusConfig):
+ super().__init__(config)
+ self.config = config
+ # This is necessary for backward compatibility, see SiglipModel initialization
+ self.vision_model = JanusVisionModel._from_config(config.vision_config)
+ self.aligner = JanusVisionAlignerMLP(self.vision_model.config)
+
+ self.vqmodel = JanusVQVAE._from_config(config.vq_config)
+
+ # Below generation_* modules are used for Image generation.
+ # Embeddings used for image generation, instead of Janus vision embeddings.
+ self.generation_embeddings = nn.Embedding(self.vqmodel.config.num_embeddings, self.vqmodel.config.embed_dim)
+ self.generation_aligner = JanusVQVAEAlignerMLP(self.vqmodel.config)
+ self.generation_head = JanusVQVAEHead(self.vqmodel.config)
+
+ self.language_model = AutoModel.from_config(config=config.text_config)
+
+ self.gradient_checkpointing = False
+ # Initialize weights and apply final processing.
+ self.post_init()
+
+ def get_input_embeddings(self):
+ return self.language_model.get_input_embeddings()
+
+ def set_input_embeddings(self, value):
+ self.language_model.set_input_embeddings(value)
+
+ @can_return_tuple
+ @auto_docstring
+ def get_image_features(
+ self, pixel_values: torch.FloatTensor, **kwargs: Unpack[TransformersKwargs]
+ ) -> tuple | BaseModelOutputWithPooling:
+ vision_outputs = self.vision_model(pixel_values, return_dict=True, **kwargs)
+ vision_outputs.pooler_output = self.aligner(vision_outputs.last_hidden_state)
+
+ return vision_outputs
+
+ def get_placeholder_mask(
+ self, input_ids: torch.LongTensor, inputs_embeds: torch.FloatTensor, image_features: torch.FloatTensor
+ ):
+ """
+ Obtains multimodal placeholder mask from `input_ids` or `inputs_embeds`, and checks that the placeholder token count is
+ equal to the length of multimodal features. If the lengths are different, an error is raised.
+ """
+ if input_ids is None:
+ special_image_mask = inputs_embeds == self.get_input_embeddings()(
+ torch.tensor(self.config.image_token_id, dtype=torch.long, device=inputs_embeds.device)
+ )
+ special_image_mask = special_image_mask.all(-1)
+ else:
+ special_image_mask = input_ids == self.config.image_token_id
+
+ n_image_tokens = special_image_mask.sum()
+ n_image_features = image_features.shape[0] * image_features.shape[1]
+ special_image_mask = special_image_mask.unsqueeze(-1).expand_as(inputs_embeds).to(inputs_embeds.device)
+ torch_compilable_check(
+ inputs_embeds[special_image_mask].numel() == image_features.numel(),
+ f"Image features and image tokens do not match, tokens: {n_image_tokens}, features: {n_image_features}",
+ )
+ return special_image_mask
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ pixel_values: torch.FloatTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ use_cache: bool | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ **kwargs,
+ ) -> JanusBaseModelOutputWithPast:
+ if (input_ids is None) ^ (inputs_embeds is not None):
+ raise ValueError(
+ "You cannot specify both input_ids and inputs_embeds at the same time, and must specify either one"
+ )
+ if inputs_embeds is None:
+ inputs_embeds = self.get_input_embeddings()(input_ids)
+
+ if pixel_values is not None:
+ image_embeds = self.get_image_features(pixel_values, return_dict=True).pooler_output
+ image_features = image_embeds.reshape(-1, inputs_embeds.shape[-1])
+ image_features = image_features.to(inputs_embeds.device, inputs_embeds.dtype)
+ image_attention_mask = self.get_placeholder_mask(
+ input_ids, inputs_embeds=inputs_embeds, image_features=image_features
+ )
+ inputs_embeds = inputs_embeds.masked_scatter(image_attention_mask, image_features)
+
+ lm_output = self.language_model(
+ inputs_embeds=inputs_embeds,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ logits_to_keep=logits_to_keep,
+ **kwargs,
+ )
+
+ return JanusBaseModelOutputWithPast(
+ last_hidden_state=lm_output.last_hidden_state,
+ past_key_values=lm_output.past_key_values,
+ hidden_states=lm_output.hidden_states,
+ attentions=lm_output.attentions,
+ image_hidden_states=image_embeds if pixel_values is not None else None,
+ )
+
+
+class JanusForConditionalGeneration(JanusPreTrainedModel, GenerationMixin):
+ _tied_weights_keys = {"lm_head.weight": "model.language_model.embed_tokens.weight"}
+ output_modalities = ("image", "text")
+ _can_compile_fullgraph = True
+
+ def __init__(self, config: JanusConfig):
+ super().__init__(config)
+ self.config = config
+ self.model = JanusModel(config)
+ self.lm_head = nn.Linear(config.text_config.hidden_size, config.text_config.vocab_size, bias=False)
+
+ # Initialize weights and apply final processing.
+ self.post_init()
+
+ def get_input_embeddings(self):
+ return self.model.language_model.get_input_embeddings()
+
+ def set_input_embeddings(self, value):
+ self.model.language_model.set_input_embeddings(value)
+
+ def prepare_embeddings_for_image_generation(self, inputs: torch.Tensor) -> torch.Tensor:
+ hidden_state = self.model.generation_embeddings(inputs)
+ hidden_state = self.model.generation_aligner(hidden_state)
+ return hidden_state
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ pixel_values: torch.FloatTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> JanusCausalLMOutputWithPast:
+ r"""
+ 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]`.
+ """
+ outputs = self.model(
+ input_ids=input_ids,
+ pixel_values=pixel_values,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ **kwargs,
+ )
+ hidden_states = outputs.last_hidden_state
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(
+ logits=logits, labels=labels, vocab_size=self.config.text_config.vocab_size, **kwargs
+ )
+
+ return JanusCausalLMOutputWithPast(
+ loss=loss,
+ logits=logits,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ image_hidden_states=outputs.image_hidden_states,
+ )
+
+ def prepare_inputs_for_generation(
+ self,
+ input_ids,
+ pixel_values=None,
+ past_key_values=None,
+ attention_mask=None,
+ inputs_embeds=None,
+ logits_to_keep=None,
+ is_first_iteration=False,
+ **kwargs,
+ ):
+ # Overwritten -- extra custom processing
+
+ model_inputs = super().prepare_inputs_for_generation(
+ input_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ attention_mask=attention_mask,
+ logits_to_keep=logits_to_keep,
+ is_first_iteration=is_first_iteration,
+ **kwargs,
+ )
+
+ # Pixel values are used only in the first iteration if available
+ # In subsequent iterations, they are already merged with text and cached
+ # NOTE: first iteration doesn't have to be prefill, it can be the first
+ # iteration with a question and cached system prompt (continue generate from cache)
+ if is_first_iteration or not kwargs.get("use_cache", True):
+ model_inputs["pixel_values"] = pixel_values
+
+ return model_inputs
+
+ def decode_image_tokens(self, image_tokens: torch.Tensor):
+ """
+ Decodes generated image tokens from language model to continuous pixel values
+ with VQGAN module via upsampling.
+ Args:
+ image_tokens (`torch.LongTensor` of shape `(batch_size, num_of_tokens)`):
+ The tensors corresponding to the input images.
+ """
+ decoded_image = self.model.vqmodel.decode(image_tokens)
+ decoded_image = decoded_image.permute(0, 2, 3, 1)
+ return decoded_image
+
+ @torch.no_grad()
+ def generate(
+ self,
+ inputs: torch.Tensor | None = None,
+ attention_mask: torch.LongTensor | None = None,
+ logits_processor: LogitsProcessorList | None = None,
+ **kwargs,
+ ):
+ # 1. Handle generation config and model kwargs
+ # Pop generation_mode first since it's specific to Janus
+ generation_mode = kwargs.pop("generation_mode", "text")
+ generation_config, model_kwargs = self._prepare_generation_config(
+ kwargs.pop("generation_config", None), **kwargs
+ )
+
+ # Default to "text" generation if mode isn't provided
+ if generation_mode == "text":
+ # Set guidance_scale=None to prevent running UnbatchedCFG processor.
+ return super().generate(
+ inputs=inputs,
+ attention_mask=attention_mask,
+ generation_config=generation_config,
+ guidance_scale=None,
+ **model_kwargs,
+ )
+
+ # Validate generation mode
+ if generation_config.get_generation_mode() not in (GenerationMode.SAMPLE, GenerationMode.GREEDY_SEARCH):
+ raise ValueError(
+ "Got incompatible mode for Image Generation, should be one of greedy or sampling. "
+ "Ensure that beam search is de-activated by setting `num_beams=1`."
+ )
+
+ # Validate the configuration and model kwargs
+ generation_config.validate()
+ self._validate_model_kwargs(model_kwargs.copy())
+
+ # 2. Initialize logit processors
+ logits_processor = logits_processor if logits_processor is not None else LogitsProcessorList()
+
+ # Set `use_cache=True` as we will be using input embeds for generation.
+ model_kwargs["use_cache"] = True
+
+ if generation_config.guidance_scale is None:
+ logger.warning("`guidance_scale` is required for CFG but not provided. Setting to default value of 5.")
+ generation_config.guidance_scale = 5
+ model_kwargs["guidance_scale"] = generation_config.guidance_scale
+
+ # 3. Prepare model inputs
+ input_ids, model_input_name, model_kwargs = self._prepare_model_inputs(
+ inputs, generation_config.bos_token_id, model_kwargs
+ )
+ dtype, device = input_ids.dtype, input_ids.device
+
+ if len(input_ids.shape) != 2:
+ raise ValueError(
+ f"Expected input ids of shape (batch_size, seq_len), but got {input_ids.shape}"
+ "Passing `inputs embeds` is not supported currently."
+ )
+
+ # Prepare special tokens which will be used generate internally.
+ kwargs_has_attention_mask = attention_mask is not None
+ self._prepare_special_tokens(generation_config, kwargs_has_attention_mask, device=input_ids.device)
+
+ # 4. Add CFG processor along with user passed logit processor.
+ if generation_config.guidance_scale and generation_config.guidance_scale > 1:
+ logits_processor.append(ClassifierFreeGuidanceLogitsProcessor(generation_config.guidance_scale))
+ generation_config.guidance_scale = None # Reset to prevent processor duplication.
+
+ # 5. Prepare logits processor
+ logits_processor = self._get_logits_processor(
+ generation_config=generation_config,
+ input_ids_seq_length=input_ids.shape[1],
+ encoder_input_ids=input_ids,
+ prefix_allowed_tokens_fn=None,
+ logits_processor=logits_processor,
+ device=device,
+ )
+
+ # 6. Expand inputs for multiple image generations per prompt.
+ input_ids, model_kwargs = self._expand_inputs_for_generation(
+ input_ids=input_ids,
+ attention_mask=attention_mask,
+ expand_size=generation_config.num_return_sequences,
+ **model_kwargs,
+ )
+
+ # 7. Prepare input and model caches
+ num_image_tokens = self.model.vision_model.config.num_image_tokens
+ batch_size, seq_len = input_ids.shape
+
+ input_tokens = input_ids.repeat(2, 1) # Double batch size for conditional/unconditional logits
+ attention_mask = model_kwargs.pop("attention_mask", None)
+ attention_mask = attention_mask.repeat(2, 1)
+ model_kwargs["attention_mask"] = attention_mask
+
+ # Mask all the tokens that are neither BOS nor BOI with pad token in the unconditional logits.
+ mask = (input_tokens[batch_size:, :] != generation_config.bos_token_id) & (
+ input_tokens[batch_size:, :] != generation_config.generation_kwargs["boi_token_id"]
+ )
+ input_tokens[batch_size:, :].masked_fill_(mask, generation_config.pad_token_id)
+
+ inputs_embeds = self.get_input_embeddings()(input_tokens)
+
+ if model_kwargs.get("past_key_values", None) is None:
+ # Prepare cache if not provided.
+ model_kwargs["past_key_values"] = self._prepare_static_cache(
+ cache_implementation=generation_config.cache_implementation or "static",
+ # batch_size should account for both conditional/unconditional input; hence multiplied by 2.
+ batch_size=batch_size * 2,
+ # we should have at least a cache len of seq_len + num_image_tokens.
+ max_cache_len=max(generation_config.max_length, num_image_tokens + seq_len),
+ model_kwargs=model_kwargs,
+ )
+
+ # Placeholder for generated tokens.
+ generated_tokens = torch.zeros((batch_size, num_image_tokens), dtype=dtype, device=device)
+
+ # 8. init attention / hidden states / scores tuples
+ output_attentions = generation_config.output_attentions
+ output_hidden_states = generation_config.output_hidden_states
+ output_scores = generation_config.output_scores
+ output_logits = generation_config.output_logits
+ return_dict_in_generate = generation_config.return_dict_in_generate
+
+ raw_scores = () if (return_dict_in_generate and output_scores) else None
+ raw_logits = () if (return_dict_in_generate and output_logits) else None
+ decoder_hidden_states = () if (return_dict_in_generate and output_hidden_states) else None
+ decoder_attentions = () if (return_dict_in_generate and output_attentions) else None
+
+ for i in range(num_image_tokens):
+ # Set `is_first_iteration=True` to force using `inputs_embeds` instead of `input_ids`.
+ # Without this, `prepare_inputs_for_generation` would use `input_ids` (the full prompt)
+ # instead of our prepared `inputs_embeds` (1 new token).
+ # This causes CUDA error: device-side assert triggered, seen around the call to ` self.self_attn`.
+ # Set this to `True` is also necessary to match the expected output, see the more detailed comment
+ # https://github.com/huggingface/transformers/pull/45044#discussion_r3020805374.
+ model_inputs = self.prepare_inputs_for_generation(
+ inputs_embeds=inputs_embeds, input_ids=input_tokens, is_first_iteration=True, **model_kwargs
+ )
+ if "attention_mask" in model_inputs:
+ model_inputs["attention_mask"] = model_inputs["attention_mask"].to(inputs_embeds.device)
+
+ outputs = self.model.language_model(
+ **model_inputs,
+ output_attentions=output_attentions,
+ output_hidden_states=output_hidden_states,
+ )
+
+ # Update model_kwargs like attention_mask for next generation.
+ model_kwargs = self._update_model_kwargs_for_generation(outputs, model_kwargs)
+ hidden_state = outputs.last_hidden_state[:, -1, :].clone()
+
+ # Generate scores using the generation head (Not using above defined LM Head)
+ scores = self.model.generation_head(hidden_state)
+ next_token_scores = logits_processor(input_ids, scores)
+
+ # Sample next token.
+ if generation_config.do_sample:
+ probs = torch.softmax(next_token_scores, dim=-1)
+ next_token = torch.multinomial(probs, num_samples=1).squeeze(-1)
+ else:
+ next_token = torch.argmax(next_token_scores, dim=-1)
+
+ generated_tokens[:, i] = next_token
+
+ # Prepare embeddings for the next step.
+ next_token = torch.cat([next_token, next_token])
+ next_token = next_token.unsqueeze(-1)
+
+ inputs_embeds = self.prepare_embeddings_for_image_generation(next_token)
+
+ if return_dict_in_generate:
+ if output_scores:
+ raw_scores += (scores,)
+ if output_logits:
+ raw_logits += (hidden_state.float(),)
+ if output_attentions:
+ decoder_attentions += outputs.attentions
+ if output_hidden_states:
+ decoder_hidden_states += outputs.hidden_states
+
+ if return_dict_in_generate:
+ return GenerateDecoderOnlyOutput(
+ sequences=generated_tokens,
+ scores=scores,
+ logits=raw_logits,
+ attentions=decoder_attentions,
+ hidden_states=decoder_hidden_states,
+ past_key_values=outputs.past_key_values,
+ )
+ else:
+ return generated_tokens
+
+
+__all__ = [
+ "JanusPreTrainedModel",
+ "JanusForConditionalGeneration",
+ "JanusModel",
+ "JanusVQVAE",
+ "JanusVisionModel",
+ "JanusVQVAEConfig",
+ "JanusVisionConfig",
+ "JanusConfig",
+]
diff --git a/third_party/transformers/src/transformers/models/janus/processing_janus.py b/third_party/transformers/src/transformers/models/janus/processing_janus.py
new file mode 100644
index 0000000000000000000000000000000000000000..bc0558b097b34193e1d0e12becf24ded7311a17a
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/janus/processing_janus.py
@@ -0,0 +1,166 @@
+# Copyright 2025 Deepseek AI and The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Processor class for Janus.
+"""
+
+from ...feature_extraction_utils import BatchFeature
+from ...image_utils import ImageInput
+from ...processing_utils import ProcessingKwargs, ProcessorMixin, TextKwargs, Unpack
+from ...tokenization_utils_base import PreTokenizedInput, TextInput
+from ...utils import auto_docstring, logging
+
+
+logger = logging.get_logger(__name__)
+
+DEFAULT_SYSTEM_PROMPT = (
+ "You are a helpful language and vision assistant. "
+ "You are able to understand the visual content that the user provides, "
+ "and assist the user with a variety of tasks using natural language.\n\n"
+)
+
+
+class JanusTextKwargs(TextKwargs, total=False):
+ """
+ generation_mode (`str`, *optional*, defaults to `"text"`):
+ The generation mode indicating which modality to generate. Can be one of `"text"` or `"image"`. When set
+ to `"text"`, the processor prepares inputs for text generation. When set to `"image"`, it prepares inputs
+ for image generation by appending image start tokens to the prompt.
+ """
+
+ generation_mode: str
+
+
+class JanusProcessorKwargs(ProcessingKwargs, total=False):
+ text_kwargs: JanusTextKwargs
+ _defaults = {
+ "text_kwargs": {"padding": False, "padding_side": "left", "generation_mode": "text"},
+ "common_kwargs": {"return_tensors": "pt"},
+ }
+
+
+@auto_docstring
+class JanusProcessor(ProcessorMixin):
+ def __init__(self, image_processor, tokenizer, chat_template=None, use_default_system_prompt=False, **kwargs):
+ r"""
+ use_default_system_prompt (`bool`, *optional*, defaults to `False`):
+ Use default system prompt for Text Generation.
+ """
+ self.num_image_tokens = 576
+ self.image_token = tokenizer.image_token
+ self.image_start_token = tokenizer.boi_token
+ self.image_end_token = tokenizer.eoi_token
+ self.use_default_system_prompt = use_default_system_prompt
+
+ super().__init__(image_processor, tokenizer, chat_template=chat_template)
+
+ @auto_docstring
+ def __call__(
+ self,
+ text: TextInput | PreTokenizedInput | list[TextInput] | list[PreTokenizedInput] = None,
+ images: ImageInput | None = None,
+ **kwargs: Unpack[JanusProcessorKwargs],
+ ) -> BatchFeature:
+ r"""
+ Returns:
+ [`BatchFeature`]: A [`BatchFeature`] with the following fields:
+
+ - **input_ids** -- List of token ids to be fed to a model. Returned when `text` is not `None`.
+ - **attention_mask** -- List of indices specifying which tokens should be attended to by the model (when
+ `return_attention_mask=True` or if *"attention_mask"* is in `self.model_input_names` and if `text` is not
+ `None`).
+ - **pixel_values** -- Pixel values to be fed to a model. Returned when `images` is not `None`.
+ """
+
+ output_kwargs = self._merge_kwargs(
+ JanusProcessorKwargs, tokenizer_init_kwargs=self.tokenizer.init_kwargs, **kwargs
+ )
+
+ if text is None and images is None:
+ raise ValueError("You must specify either text or images.")
+
+ if text is not None:
+ if isinstance(text, str):
+ text = [text]
+ elif not (isinstance(text, (list, tuple)) and all(isinstance(t, str) for t in text)):
+ raise ValueError("Invalid input text. Please provide a string, or a list of strings")
+
+ generation_mode = output_kwargs["text_kwargs"].pop("generation_mode")
+
+ # Replace the image token with expanded image tokens.
+ prompt_strings = []
+ one_img_tokens = self.image_start_token + (self.image_token * self.num_image_tokens) + self.image_end_token
+ for prompt in text:
+ prompt = prompt.replace(self.image_token, one_img_tokens)
+ if self.use_default_system_prompt and generation_mode == "text":
+ prompt = DEFAULT_SYSTEM_PROMPT + prompt
+ if generation_mode == "image":
+ prompt += self.image_start_token
+ prompt_strings.append(prompt)
+
+ data = self.tokenizer(prompt_strings, **output_kwargs["text_kwargs"])
+
+ # Process images if pixel values are provided.
+ if images is not None and generation_mode != "image":
+ data["pixel_values"] = self.image_processor(images=images, **output_kwargs["images_kwargs"])[
+ "pixel_values"
+ ]
+
+ return BatchFeature(data=data)
+
+ def postprocess(self, images: ImageInput, **kwargs):
+ """
+ Forwards all arguments to the image processor's `postprocess` method.
+ Refer to the original method's docstring for more details.
+ """
+ return self.image_processor.postprocess(images, **kwargs)
+
+ def post_process_multimodal_output(
+ self, generated_outputs, skip_special_tokens=True, generation_mode=None, **kwargs
+ ):
+ """
+ Post-process the output of a multimodal model to return the requested modality output.
+ If the model cannot generated the requested modality, an error will be raised.
+
+ Args:
+ generated_outputs (`torch.Tensor` or `np.ndarray`):
+ The output of the model `generate` function. The output is expected to be a tensor of shape `(batch_size, sequence_length)`
+ or `(sequence_length,)`.
+ skip_special_tokens (`bool`, *optional*, defaults to `True`):
+ Whether or not to remove special tokens in the output. Argument passed to the tokenizer's `batch_decode` method.
+ generation_mode (`str`, *optional*):
+ Generation mode indicated which modality to output and can be one of `["text", "image", "audio"]`.
+ **kwargs:
+ Additional arguments to be passed to the tokenizer's `batch_decode method`.
+
+ Returns:
+ `list[Union[str, PIL.Image.Image]]`: The decoded text or generated image.
+ """
+ if generation_mode is None or generation_mode == "text":
+ return self.post_process_image_text_to_text(
+ generated_outputs, skip_special_tokens=skip_special_tokens, **kwargs
+ )
+
+ elif generation_mode == "image":
+ generated_outputs = list(generated_outputs.float())
+ images = self.postprocess(generated_outputs, return_tensors="PIL.Image.Image")
+ return images["pixel_values"]
+
+ else:
+ raise ValueError(
+ f"{self.__class__.__name__} got an unexpected generation_mode={generation_mode}. Supported options are only `text` and `image"
+ )
+
+
+__all__ = ["JanusProcessor"]
diff --git a/third_party/transformers/src/transformers/models/llama4/__init__.py b/third_party/transformers/src/transformers/models/llama4/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..bc1c686afd7f96db0a05e34c4d0e3b58d49e2944
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/llama4/__init__.py
@@ -0,0 +1,29 @@
+# Copyright 2025 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_llama4 import *
+ from .image_processing_llama4 import *
+ from .modeling_llama4 import *
+ from .processing_llama4 import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/llama4/configuration_llama4.py b/third_party/transformers/src/transformers/models/llama4/configuration_llama4.py
new file mode 100644
index 0000000000000000000000000000000000000000..79cfd063f4d4d8ffb5e634238f244d4a65442b46
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/llama4/configuration_llama4.py
@@ -0,0 +1,261 @@
+# Copyright 2025 The LLAMA4 and HuggingFace Inc. team. All rights reserved.
+#
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...modeling_rope_utils import RopeParameters
+from ...utils import auto_docstring, logging
+
+
+logger = logging.get_logger(__name__)
+
+
+@auto_docstring(checkpoint="meta-llama/Llama-4-Scout-17B-16E")
+@strict
+class Llama4VisionConfig(PreTrainedConfig):
+ r"""
+ vision_output_dim (`int`, *optional*, defaults to 7680):
+ Dimensionality of the vision model output. Includes output of transformer
+ encoder with intermediate layers and global transformer encoder.
+ pixel_shuffle_ratio (`float`, *optional*, defaults to 0.5):
+ Pixel-shuffle ratio for downsampling patch tokens. Smaller values produce fewer tokens (more downsampling).
+ projector_input_dim (`int`, *optional*, defaults to 4096):
+ Width of the vision adapter MLP before pixel shuffle. Larger value increases capacity and compute.
+ projector_output_dim (`int`, *optional*, defaults to 4096):
+ Output width of the vision adapter. Larger value yields higher-dimensional image features.
+ projector_dropout (`float`, *optional*, defaults to 0.0):
+ Dropout rate inside the vision adapter MLP. Higher value adds more regularization.
+ """
+
+ base_model_tp_plan = {
+ "model.layers.*.self_attn.q_proj": "colwise",
+ "model.layers.*.self_attn.k_proj": "colwise",
+ "model.layers.*.self_attn.v_proj": "colwise",
+ "model.layers.*.self_attn.o_proj": "rowwise",
+ "vision_adapter.mlp.fc1": "colwise",
+ "vision_adapter.mlp.fc2": "rowwise",
+ "patch_embedding.linear": "colwise_gather_output",
+ }
+ model_type = "llama4_vision_model"
+ base_config_key = "vision_config"
+
+ hidden_size: int = 768
+ hidden_act: str = "gelu"
+ num_hidden_layers: int = 34
+ num_attention_heads: int = 16
+ num_channels: int = 3
+ intermediate_size: int = 5632
+ vision_output_dim: int = 7680
+ image_size: int | list[int] | tuple[int, int] = 448
+ patch_size: int | list[int] | tuple[int, int] = 14
+ norm_eps: float = 1e-5
+ vision_feature_select_strategy: str = "default"
+ initializer_range: float = 0.02
+ pixel_shuffle_ratio: float = 0.5
+ projector_input_dim: int = 4096
+ projector_output_dim: int = 4096
+ multi_modal_projector_bias: bool = False
+ projector_dropout: float | int = 0.0
+ attention_dropout: float | int = 0.0
+ rope_parameters: RopeParameters | dict | None = None
+
+
+@auto_docstring(checkpoint="meta-llama/Llama-4-Scout-17B-16E")
+@strict
+class Llama4TextConfig(PreTrainedConfig):
+ r"""
+ intermediate_size_mlp (`int`, *optional*, defaults to 16384):
+ Intermediate size of dense MLP layers. Larger value increases FFN capacity and compute.
+ moe_layers (`list[int]`, *optional*):
+ List of layer indices that use MoE. Overrides `interleave_moe_layer_step` when set.
+ interleave_moe_layer_step (`int`, *optional*, defaults to 1):
+ Spacing between MoE layers when `moe_layers` is `None`. Larger value means fewer MoE layers.
+ use_qk_norm (`bool`, *optional*, defaults to `True`):
+ Whether to L2-normalize queries/keys on RoPE layers. Can stabilize attention when enabled.
+ no_rope_layers (`list[int]`, *optional*):
+ List with at least the same length as the number of layers in the model.
+ A `1` at an index position indicates that the corresponding layer will use RoPE,
+ while a `0` indicates that it's a NoPE layer.
+ no_rope_layer_interval (`int`, *optional*, defaults to 4):
+ If `no_rope_layers` is `None`, it will be created using a NoPE layer every
+ `no_rope_layer_interval` layers.
+ attention_chunk_size (`int`, *optional*, defaults to 8192):
+ Chunk size for the attention computation. Smaller value enforces more local attention and lowers memory.
+ attn_temperature_tuning (`bool`, *optional*, defaults to `True`):
+ Whether to dynamically scale the attention temperature for each query token based on sequence length.
+ Recommended for long sequences (e.g., >32k tokens) to maintain stable output results.
+ floor_scale (`int`, *optional*, defaults to 8192):
+ Base scale (in tokens) for attention temperature tuning. Larger value delays scaling to longer positions.
+ attn_scale (`float`, *optional*, defaults to 0.1):
+ Strength of attention temperature tuning. Larger value increases scaling at long positions.
+
+ Example:
+ """
+
+ model_type = "llama4_text"
+ keys_to_ignore_at_inference = ["past_key_values"]
+ default_theta = 500000.0
+ base_model_tp_plan = {
+ "layers.*.self_attn.q_proj": "colwise",
+ "layers.*.self_attn.k_proj": "colwise",
+ "layers.*.self_attn.v_proj": "colwise",
+ "layers.*.self_attn.o_proj": "rowwise",
+ "layers.*.feed_forward.shared_expert.gate_proj": "colwise",
+ "layers.*.feed_forward.shared_expert.up_proj": "colwise",
+ "layers.*.feed_forward.shared_expert.down_proj": "rowwise",
+ "layers.*.feed_forward.experts.gate_up_proj": "packed_rowwise", # row because not linear
+ "layers.*.feed_forward.experts.down_proj": "colwise", # col because not linear
+ "layers.*.feed_forward.gate_proj": "colwise",
+ "layers.*.feed_forward.up_proj": "colwise",
+ "layers.*.feed_forward.down_proj": "rowwise",
+ }
+ base_model_ep_plan = {
+ "layers.*.self_attn.q_proj": "colwise",
+ "layers.*.self_attn.k_proj": "colwise",
+ "layers.*.self_attn.v_proj": "colwise",
+ "layers.*.self_attn.o_proj": "rowwise",
+ "layers.*.feed_forward.experts.gate_up_proj": "grouped_gemm", # row because not linear
+ "layers.*.feed_forward.experts.down_proj": "grouped_gemm", # col because not linear
+ "layers.*.feed_forward.gate_proj": "colwise",
+ "layers.*.feed_forward.up_proj": "colwise",
+ "layers.*.feed_forward.down_proj": "rowwise",
+ "layers.*.feed_forward.router": "ep_router",
+ }
+
+ vocab_size: int = 202048
+ hidden_size: int = 5120
+ intermediate_size: int = 8192
+ intermediate_size_mlp: int = 16384
+ num_hidden_layers: int = 48
+ num_attention_heads: int = 40
+ num_key_value_heads: int = 8
+ head_dim: int = 128
+ hidden_act: str = "silu"
+ max_position_embeddings: int = 4096 * 32
+ initializer_range: float = 0.02
+ rms_norm_eps: float = 1e-5
+ use_cache: bool = True
+ pad_token_id: int | None = None
+ bos_token_id: int | None = 1
+ eos_token_id: int | list[int] | None = 2
+ tie_word_embeddings: bool = False
+ attention_dropout: float | int = 0.0
+ num_experts_per_tok: int = 1
+ num_local_experts: int = 16
+ moe_layers: list[int] | None = None
+ interleave_moe_layer_step: int = 1
+ use_qk_norm: bool = True
+ output_router_logits: bool = False
+ router_aux_loss_coef: float = 0.001
+ router_jitter_noise: float = 0.0
+ rope_parameters: RopeParameters | dict | None = None
+ no_rope_layers: list[int] | None = None
+ no_rope_layer_interval: int = 4
+ attention_chunk_size: int | None = 8192
+ layer_types: list[str] | None = None
+ attn_temperature_tuning: bool = True
+ floor_scale: int = 8192
+ attn_scale: float = 0.1
+ attention_bias: bool = False
+
+ def __post_init__(self, **kwargs):
+ if self.num_key_value_heads is None:
+ self.num_key_value_heads = self.num_attention_heads
+
+ default_no_rope_layers = [
+ int((layer_idx + 1) % self.no_rope_layer_interval != 0) for layer_idx in range(self.num_hidden_layers)
+ ]
+ self.no_rope_layers = self.no_rope_layers if self.no_rope_layers else default_no_rope_layers
+ self.head_dim = self.head_dim if self.head_dim is not None else self.hidden_size // self.num_attention_heads
+
+ self.moe_layers = (
+ self.moe_layers
+ if self.moe_layers is not None
+ else list(
+ range(
+ self.interleave_moe_layer_step - 1,
+ self.num_hidden_layers,
+ self.interleave_moe_layer_step,
+ )
+ )
+ )
+
+ if self.layer_types is None:
+ self.layer_types = [
+ "chunked_attention" if no_rope else "full_attention" for no_rope in self.no_rope_layers
+ ]
+
+ super().__post_init__(**kwargs)
+
+
+@auto_docstring(checkpoint="meta-llama/Llama-4-Scout-17B-16E")
+@strict
+class Llama4Config(PreTrainedConfig):
+ r"""
+ boi_token_index (`int`, *optional*, defaults to 200080):
+ The begin-of-image token index to wrap the image prompt.
+ eoi_token_index (`int`, *optional*, defaults to 200081):
+ The end-of-image token index to wrap the image prompt.
+
+ ```python
+ >>> from transformers import Llama4Model, Llama4Config
+
+ >>> # Initializing a Llama4 7B style configuration
+ >>> configuration = Llama4Config()
+
+ >>> # Initializing a model from the Llama4 7B style configuration
+ >>> model = Llama4Model(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```
+ """
+
+ model_type = "llama4"
+ attribute_map = {
+ "image_token_id": "image_token_index",
+ "boi_token_id": "boi_token_index",
+ "eoi_token_id": "eoi_token_index",
+ }
+ sub_configs = {"text_config": Llama4TextConfig, "vision_config": Llama4VisionConfig}
+ base_model_tp_plan = {
+ "multi_modal_projector.linear_1": "colwise_rep",
+ }
+
+ vision_config: dict | PreTrainedConfig | None = None
+ text_config: dict | PreTrainedConfig | None = None
+ boi_token_index: int = 200080
+ eoi_token_index: int = 200081
+ image_token_index: int = 200092
+ tie_word_embeddings: bool = False
+
+ def __post_init__(self, **kwargs):
+ if self.vision_config is None:
+ self.vision_config = Llama4VisionConfig()
+ logger.info("vision_config is None, using default llama4 vision config")
+ elif isinstance(self.vision_config, dict):
+ self.vision_config = Llama4VisionConfig(**self.vision_config)
+
+ if self.text_config is None:
+ self.text_config = Llama4TextConfig()
+ logger.info("text_config is None, using default llama4 text config")
+ elif isinstance(self.text_config, dict):
+ self.text_config = Llama4TextConfig(**self.text_config)
+ super().__post_init__(**kwargs)
+
+
+__all__ = ["Llama4Config", "Llama4TextConfig", "Llama4VisionConfig"]
diff --git a/third_party/transformers/src/transformers/models/llama4/convert_llama4_weights_to_hf.py b/third_party/transformers/src/transformers/models/llama4/convert_llama4_weights_to_hf.py
new file mode 100644
index 0000000000000000000000000000000000000000..4ceab6067b4c635f58b892203be3a006ef613d8f
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/llama4/convert_llama4_weights_to_hf.py
@@ -0,0 +1,737 @@
+import argparse
+import gc
+import io
+import json
+import os
+import re
+
+import torch
+from tokenizers import AddedToken, processors
+from tqdm import tqdm
+
+from transformers import (
+ GenerationConfig,
+ Llama4Config,
+ Llama4ForConditionalGeneration,
+ Llama4ImageProcessorFast,
+ Llama4Processor,
+ Llama4TextConfig,
+ Llama4VisionConfig,
+ PreTrainedTokenizerFast,
+)
+from transformers.integrations.tiktoken import TikTokenConverter
+
+
+_OFFLINE_QUANT_COMPATIBLE = os.environ.get("OFFLINE_QUANT_COMPATIBLE", "0") == "1"
+
+torch.serialization.add_safe_globals([io.BytesIO])
+# fmt: off
+# `None` means we drop the key
+
+
+weight_postfix = ".weight" if _OFFLINE_QUANT_COMPATIBLE else ""
+ORIGINAL_TO_CONVERTED_KEY_MAPPING = {
+ # CausalLM keys
+ r"output.weight": r"language_model.lm_head.weight",
+ r"\nnorm.weight": r"\nlanguage_model.model.norm.weight",
+ # Model keys
+ r"tok_embeddings.weight": r"language_model.model.embed_tokens.weight",
+ r"freq_cis": None,
+ r"rope.freqs": None,
+ r"layers.(\d+).attention_norm.weight": r"language_model.model.layers.\1.input_layernorm.weight",
+ r"layers.(\d+).attention.wqkv.layer_norm_weight": r"language_model.model.layers.\1.input_layernorm.weight",
+ r"layers.(\d+).feed_forward.norm.weight": r"language_model.model.layers.\1.post_attention_layernorm.weight",
+ r"layers.(\d+).attention.wo.weight": r"language_model.model.layers.\1.self_attn.o_proj.weight",
+ r"layers.(\d+).attention.wqkv.weight": r"language_model.model.layers.\1.self_attn.qkv_proj.weight",
+
+ # MoE keys: no simple MLPmodel.
+ r"layers.(\d+).feed_forward.experts.moe_w_in_eD_F": r"language_model.model.layers.\1.feed_forward.experts.gate_proj" + weight_postfix, # will be fused with up
+ r"layers.(\d+).feed_forward.experts.moe_w_out_eF_D": r"language_model.model.layers.\1.feed_forward.experts.down_proj" + weight_postfix, # expert win
+ r"layers.(\d+).feed_forward.experts.moe_w_swiglu_eD_F": r"language_model.model.layers.\1.feed_forward.experts.up_proj" + weight_postfix, # fused with up
+ r"layers.(\d+).feed_forward.router_DE": r"language_model.model.layers.\1.feed_forward.router.weight", # used for top
+ r"layers.(\d+).feed_forward.w_in_shared_FD": r"language_model.model.layers.\1.feed_forward.shared_expert.gate_proj", # might need to be fused for efficiency?
+ r"layers.(\d+).feed_forward.w_out_shared_DF": r"language_model.model.layers.\1.feed_forward.shared_expert.down_proj", # might need to be fused for efficiency?
+ r"layers.(\d+).feed_forward.w_swiglu_FD": r"language_model.model.layers.\1.feed_forward.shared_expert.up_proj", # might need to be fused for efficiency?
+ r"layers.(\d+).feed_forward.global_gate_stats_3E": None,
+ # Unused keys in load hooks (explicitly removed)
+ r'layers.(\d+).attention.wqkv._extra_state': None,
+ r'layers.(\d+).attention.wo._extra_state': None,
+ # Key apparently unused in base models
+ r'layers.(\d+).feed_forward.expert_activation_DE': None,
+
+ # MLP layer variant
+ r"layers.(\d+).feed_forward.w1.weight": r"language_model.model.layers.\1.feed_forward.gate_proj.weight", # might need to be fused for efficiency?
+ r"layers.(\d+).feed_forward.w3.weight": r"language_model.model.layers.\1.feed_forward.up_proj.weight", # might need to be fused for efficiency?
+ # r"layers.(\d+).feed_forward.mlp.fc1_weight": r"language_model.model.layers.\1.feed_forward.gate_up_proj.weight",
+ r"layers.(\d+).feed_forward.mlp.fc2_weight": r"language_model.model.layers.\1.feed_forward.down_proj.weight",
+ r"layers.(\d+).feed_forward.w2.weight": r"language_model.model.layers.\1.feed_forward.down_proj.weight",
+ r"layers.(\d+).feed_forward.mlp.layer_norm.weight": r"language_model.model.layers.\1.post_attention_layernorm.weight",
+
+ # Vision encoder mapping
+ r"vision_embeddings.vision_encoder.conv1._linear": r"vision_model.patch_embedding.linear",
+ r'vision_embeddings.vision_adapter.mlp.c_fc': r"vision_model.vision_adapter.mlp.fc1",
+ r'vision_embeddings.vision_adapter.mlp.c_proj': r"vision_model.vision_adapter.mlp.fc2",
+ r"vision_embeddings.vision_encoder.transformer.resblocks.(\d+).attn.wq.(weight|bias)": r"vision_model.model.layers.\1.self_attn.q_proj.\2",
+ r"vision_embeddings.vision_encoder.transformer.resblocks.(\d+).attn.wk.(weight|bias)": r"vision_model.model.layers.\1.self_attn.k_proj.\2",
+ r"vision_embeddings.vision_encoder.transformer.resblocks.(\d+).attn.wv.(weight|bias)": r"vision_model.model.layers.\1.self_attn.v_proj.\2",
+ r"vision_embeddings.vision_encoder.transformer.resblocks.(\d+).attn.wo.(weight|bias)": r"vision_model.model.layers.\1.self_attn.o_proj.\2",
+ r"vision_embeddings.vision_encoder.transformer.resblocks.(\d+).mlp.c_fc": r"vision_model.model.layers.\1.mlp.fc1",
+ r"vision_embeddings.vision_encoder.transformer.resblocks.(\d+).mlp.c_proj": r"vision_model.model.layers.\1.mlp.fc2",
+ r"vision_embeddings.vision_encoder.transformer.resblocks.(\d+).ln_1.(weight|bias)": r"vision_model.model.layers.\1.input_layernorm.\2",
+ r"vision_embeddings.vision_encoder.transformer.resblocks.(\d+).ln_2.(weight|bias)": r"vision_model.model.layers.\1.post_attention_layernorm.\2",
+ # r'vision_embeddings.vision_encoder.ln_(1|2).(weight|bias)': r'vision_model.transformer.vision_encoder.layernorm_\1.\2',
+ r'vision_embeddings.vision_encoder.ln_post': r'vision_model.layernorm_post',
+ r'vision_embeddings.vision_encoder.ln_pre': r'vision_model.layernorm_pre',
+ r'vision_embeddings.vision_encoder.class_embedding': r'vision_model.class_embedding',
+ r"vision_embeddings.vision_encoder.positional_embedding_vlm": r"vision_model.positional_embedding_vlm",
+ r"vision_embeddings.vision_encoder.(?=\w)": r"vision_model.model.",
+ r"vision_projection.weight": r"multi_modal_projector.linear_1.weight",
+}
+# fmt: on
+
+
+def convert_old_keys_to_new_keys(state_dict_keys: dict | None = None):
+ """
+ This function should be applied only once, on the concatenated keys to efficiently rename using
+ the key mappings.
+ """
+ output_dict = {}
+ if state_dict_keys is not None:
+ old_text = "\n".join(state_dict_keys)
+ new_text = old_text
+ for pattern, replacement in ORIGINAL_TO_CONVERTED_KEY_MAPPING.items():
+ if replacement is None:
+ new_text = re.sub(pattern, "", new_text) # an empty line
+ continue
+ new_text = re.sub(pattern, replacement, new_text)
+ output_dict = dict(zip(old_text.split("\n"), new_text.split("\n")))
+ return output_dict
+
+
+def permute_for_rope(input_tensor, n_heads, dim1, dim2):
+ """
+ When you go from the complex ROPE formulation to sin and cos one, you need
+ to permute the query and key weights (to avoid doing it on the fly)
+ """
+ input_tensor = input_tensor.view(n_heads, dim1 // n_heads // 2, 2, dim2)
+ input_tensor = input_tensor.transpose(1, 2).reshape(dim1, dim2)
+ return input_tensor
+
+
+def is_param_same_across_shards(key):
+ """
+ Return `False` if the parameter is different across checkpoint shards
+ and needs to be concatenated.
+ """
+ patterns = [
+ r"language_model.layers.(\d+).(.*)layernorm.weight",
+ r"language_model.norm.weight",
+ r"router.weight",
+ r"feed_forward.global_gate_stats",
+ # not all vision weights are sharded, some are repeated
+ r"vision_model.class_embedding",
+ r"vision_model.positional_embedding_vlm",
+ r"vision_embeddings.vision_encoder.positional_embedding_vlm",
+ r"vision_model.model.layers.(\d+).self_attn.o_proj.bias",
+ r"vision_model.model.layers.(\d+).input_layernorm",
+ r"vision_model.model.layers.(\d+).post_attention_layernorm",
+ r"vision_model.layernorm_pre",
+ r"vision_model.layernorm_post",
+ r"vision_model.model.layers.(\d+).mlp.fc2.bias",
+ r"norm.weight",
+ ] # fmt: skip
+ return any(re.search(pattern, key) for pattern in patterns)
+
+
+def get_concat_dim(key):
+ """
+ Return the dimension to concatenate the weights on.
+ """
+ concat_dim_1 = [
+ # language dim 1 sharded weights
+ "feed_forward.router.weight",
+ "self_attn.o_proj",
+ "experts.gate_proj",
+ "experts.up_proj",
+ "expert.down_proj",
+ # "feed_forward.up_proj",
+ # "feed_forward.gate_proj",
+ "feed_forward.down_proj",
+ "global_gate_stats",
+ # vision dim1 sharded stuff
+ "mlp.fc2.weight", # covers all rowparallels across vis
+ ] # fmt: off
+ if any(re.search(pattern, key) for pattern in concat_dim_1):
+ return 1
+ return 0
+
+
+def compute_intermediate_size(hidden_dim, ffn_exp=4, multiple_of=1024, ffn_dim_multiplier=1.2):
+ hidden_dim = ffn_exp * int(2 * hidden_dim / 3)
+ hidden_dim = int(ffn_dim_multiplier * hidden_dim)
+ hidden_dim = multiple_of * ((hidden_dim + multiple_of - 1) // multiple_of)
+ return hidden_dim
+
+
+# Ignore extra info - h/t Aritra
+def safe_load(filename):
+ # Can use weights_only because io.BytesIO was registered, but we still need to skip those objects
+ shard = torch.load(filename, weights_only=True, map_location="cpu", mmap=True)
+ shard = {k: v for k, v in shard.items() if not isinstance(v, io.BytesIO)}
+ return shard
+
+
+# Unpack mlp projections - possibly to be removed when they are fused
+def preprocess_keys(state_dict):
+ new_state_dict = {}
+ for key, value in state_dict.items():
+ if "mlp.fc1_weight" in key:
+ prefix = key.split("mlp.fc1_weight")[0]
+ w1, w3 = value.chunk(2, dim=0)
+ new_state_dict[prefix + "w1.weight"] = w1
+ new_state_dict[prefix + "w3.weight"] = w3
+ else:
+ new_state_dict[key] = value
+ return new_state_dict
+
+
+def max_context_length(model_path, instruct=False):
+ """256K for base, 1M for 128E instruct, 10M for 16E instruct."""
+ if not instruct:
+ return 256 * 1024
+
+ with open(os.path.join(model_path, "params.json"), "r") as f:
+ params = json.load(f)
+ params = params.get("model", params)
+ if params.get("moe_args") is None:
+ return 8192
+ num_experts = params["moe_args"]["num_experts"]
+ return 10485760 if num_experts == 16 else 1048576
+
+
+def write_model(
+ model_path,
+ input_base_path,
+ num_shards,
+ convert_checkpoints,
+ instruct=False,
+):
+ os.makedirs(model_path, exist_ok=True)
+
+ with open(os.path.join(input_base_path, "params.json"), "r") as f:
+ params = json.load(f)
+
+ params = params.get("model", params)
+ dtype = "bfloat16"
+
+ # ------------------------------------------------------------
+ # Text model params and config
+ # ------------------------------------------------------------
+
+ # params from config
+ vocab_size = 202048 # params["vocab_size"] # seems like the lm head is 25256 so padded instead of 202048
+ num_layers = params["n_layers"]
+ dim = params["dim"]
+ num_heads = params["n_heads"]
+ rms_norm_eps = params["norm_eps"]
+ rope_theta = params["rope_theta"]
+ no_rope_layer_interval = params["nope_layer_interval"]
+ attention_chunk_size = params["attention_chunk_size"]
+
+ config_kwargs = {}
+ if params["use_scaled_rope"]:
+ # some constants from original code
+ rope_parameters = {
+ "rope_type": "llama3",
+ "factor": params.get("rope_parameters_factor", 8.0),
+ "low_freq_factor": 1.0,
+ "high_freq_factor": params.get("rope_high_freq_factor", 4.0),
+ "original_max_position_embeddings": 8192,
+ }
+ config_kwargs.update({"rope_parameters": rope_parameters})
+
+ if attention_chunk_size is None:
+ config_kwargs.update({"cache_implementation": "static"})
+
+ # compute additional params for weight conversion
+ num_heads_per_shard = num_heads // num_shards
+ dim_per_head = dim // num_heads
+ intermediate_size_mlp = compute_intermediate_size(
+ dim,
+ ffn_exp=params["ffn_exp"],
+ multiple_of=params["multiple_of"],
+ ffn_dim_multiplier=params["ffn_dim_multiplier"],
+ )
+
+ num_key_value_heads = params["n_kv_heads"] # for GQA / MQA
+
+ if params.get("moe_args", False):
+ num_experts = params["moe_args"]["num_experts"]
+ interleave_moe_layer_step = params["moe_args"].get("interleave_moe_layer_step", 1)
+ else:
+ # Dense model (possibly Llama Guard) - disable all moe layers
+ num_experts = 0
+ interleave_moe_layer_step = 0
+ config_kwargs.update({"moe_layers": []})
+
+ # Ensure all layers are rope if `nope_layer_interval` is None
+ no_rope_layer_interval = params["nope_layer_interval"]
+ no_rope_layer_interval = num_heads * 2 if no_rope_layer_interval is None else no_rope_layer_interval
+
+ bos_token_id = 200000
+ eos_token_id = [200001, 200007, 200008] if instruct else 200001
+ pad_token_id = 200018
+
+ text_config = Llama4TextConfig(
+ num_attention_heads=num_heads,
+ vocab_size=vocab_size,
+ hidden_size=dim,
+ rms_norm_eps=rms_norm_eps,
+ rope_theta=rope_theta,
+ num_hidden_layers=num_layers,
+ intermediate_size=8192,
+ intermediate_size_mlp=intermediate_size_mlp,
+ max_position_embeddings=max_context_length(input_base_path, instruct),
+ num_local_experts=num_experts,
+ interleave_moe_layer_step=interleave_moe_layer_step,
+ use_qk_norm=params["use_qk_norm"],
+ no_rope_layer_interval=no_rope_layer_interval,
+ attention_chunk_size=attention_chunk_size,
+ bos_token_id=bos_token_id,
+ eos_token_id=eos_token_id,
+ pad_token_id=pad_token_id,
+ tie_word_embeddings=False, # Constant set to False
+ dtype=dtype,
+ for_llm_compressor=_OFFLINE_QUANT_COMPATIBLE,
+ **config_kwargs,
+ )
+ # default vision config from params
+
+ vision_params = params["vision_args"]
+ vision_dim = vision_params["dim"]
+ vision_num_layers = vision_params["n_layers"]
+ image_size = vision_params["image_size"]["height"] # siglip config is outdated
+ vision_num_heads = vision_params["n_heads"]
+
+ vision_output_dim = vision_params["output_dim"]
+
+ vision_config = Llama4VisionConfig(
+ hidden_act="gelu",
+ num_hidden_layers=vision_num_layers,
+ image_size=image_size,
+ num_attention_heads=vision_num_heads,
+ hidden_size=vision_dim,
+ vision_output_dim=vision_output_dim,
+ )
+
+ config = Llama4Config(text_config=text_config, vision_config=vision_config)
+ config.save_pretrained(model_path)
+
+ print("Model config saved successfully...")
+
+ # ------------------------------------------------------------
+ # Convert weights
+ # ------------------------------------------------------------
+
+ if convert_checkpoints:
+ print(f"Fetching all parameters from the checkpoint at {input_base_path}...")
+ if num_shards == 1:
+ if os.path.exists(os.path.join(input_base_path, "consolidated.00.pth")):
+ path = os.path.join(input_base_path, "consolidated.00.pth")
+ else:
+ path = os.path.join(input_base_path, "consolidated.pth")
+ loaded = [safe_load(path)]
+ else:
+ loaded = [
+ safe_load(os.path.join(input_base_path, f"consolidated.{i:02d}.pth"))
+ for i in tqdm(range(num_shards), desc="Loading shards", unit="shard")
+ ]
+ loaded = [preprocess_keys(d) for d in loaded]
+
+ all_keys_raw = list(loaded[0].keys())
+ repeated_keys = []
+ sharded_keys = []
+ for _key in all_keys_raw:
+ try:
+ if num_shards == 1 or (loaded[0][_key] == loaded[1][_key]).all():
+ repeated_keys.append(_key)
+ else:
+ sharded_keys.append(_key)
+ except Exception as e:
+ print(f"Encountered exception {e} for {_key}")
+ print("Initializing an empty model")
+ with torch.device("meta"):
+ model = Llama4ForConditionalGeneration(config)
+
+ print("Converting model...")
+ all_keys = list(loaded[0].keys())
+ new_keys = convert_old_keys_to_new_keys(all_keys)
+ state_dict = {}
+ replicated_params = [] # To keep track of replicated weights.
+ for key in tqdm(all_keys, desc="Renaming and processing all keys", unit="key"):
+ new_key = new_keys[key]
+ print(key, new_key)
+ if num_shards > 1 and not is_param_same_across_shards(new_key):
+ current_parameter = [chunk.pop(key) for chunk in loaded if not isinstance(chunk[key], io.BytesIO)]
+ else:
+ print(f"{key} (now {new_key}) is the same across all shards.")
+ replicated_params.append((key, new_key))
+ current_parameter = [loaded[0].pop(key)] if not isinstance(loaded[0][key], io.BytesIO) else []
+
+ if "running_gate_stats_3E" in key:
+ new_keys.pop(new_key)
+ continue
+
+ concat_dim = get_concat_dim(new_key)
+
+ # Post-process the current_parameter.
+ if "qkv_proj" in new_key:
+ queries = []
+ keys = []
+ values = []
+ for param in current_parameter:
+ query, key_, value = param.split(
+ [
+ num_heads * dim_per_head // num_shards,
+ num_key_value_heads * dim_per_head // num_shards,
+ num_key_value_heads * dim_per_head // num_shards,
+ ]
+ )
+ queries.append(query.reshape(num_heads_per_shard, -1, dim))
+ keys.append(key_.reshape(num_key_value_heads // num_shards, -1, dim))
+ values.append(value.reshape(num_key_value_heads // num_shards, -1, dim))
+
+ queries = torch.cat(queries, dim=0).reshape(dim, dim)
+ keys = torch.cat(keys, dim=0).reshape(num_key_value_heads * dim_per_head, dim)
+ values = torch.cat(values, dim=0).reshape(num_key_value_heads * dim_per_head, dim)
+ # queries = permute_for_rope(queries, num_heads, dim, dim)
+ # keys = permute_for_rope(keys, num_key_value_heads, num_key_value_heads*dim_per_head, dim)
+
+ q = new_key.replace("qkv", "q")
+ tqdm.write(f"Processing: {key.ljust(50)} ->\t {q}, {queries.shape}")
+ state_dict[q] = queries
+
+ k = new_key.replace("qkv", "k")
+ tqdm.write(f"Processing: {key.ljust(50)} ->\t {k}, {keys.shape}")
+ state_dict[k] = keys
+
+ v = new_key.replace("qkv", "v")
+ tqdm.write(f"Processing: {key.ljust(50)} ->\t {v}, {values.shape}")
+ state_dict[v] = values
+ elif _OFFLINE_QUANT_COMPATIBLE and "feed_forward.experts." in new_key:
+ # for experts, we need to split expert for offline quantization purpose and don't need to fuse
+ expert_lists = []
+ for k in current_parameter:
+ expert_lists.append(
+ list(k.reshape(num_experts, -1, k.shape[-1]).unbind(0))
+ ) # [#expert * IN, OUT] -> #experts * [IN, OUT]
+ for i in range(num_experts):
+ expert = torch.cat([expert_list[i] for expert_list in expert_lists], dim=concat_dim)
+ expert_key = new_key.replace("experts.", f"experts.{i}.")
+ state_dict[expert_key] = expert.transpose(0, 1).contiguous() # [OUT, IN]
+ tqdm.write(f"Processing: {key.ljust(50)} ->\t {expert_key}, {state_dict[expert_key].shape}")
+ elif re.search(r"(gate|up)_proj", new_key):
+ path = new_key.split(".")
+ gate_key = re.sub(r"(gate|up)_proj", lambda m: "gate_proj", new_key)
+ up_key = re.sub(r"(gate|up)_proj", lambda m: "up_proj", new_key)
+ if gate_key == new_key:
+ state_dict[new_key] = torch.cat(current_parameter, dim=concat_dim)
+ elif new_key == up_key:
+ if "experts" not in new_key:
+ state_dict[new_key] = torch.cat(current_parameter, dim=concat_dim)
+ else:
+ gate_proj = state_dict.pop(gate_key)
+ gate_proj = [
+ gate_proj.reshape(num_experts, -1, 8, 1024)[:, :, k, :].reshape(num_experts, -1, 1024)
+ for k in range(8)
+ ]
+ gate_proj = torch.cat(gate_proj, dim=-1)
+
+ up_proj = [
+ k.reshape(num_experts, -1, 8, 1024).reshape(num_experts, -1, 1024)
+ for k in current_parameter
+ ]
+ up_proj = torch.cat(up_proj, dim=-1)
+
+ gate_up_proj = torch.cat((gate_proj, up_proj), dim=-1)
+ new_key = new_key.replace("up_proj", "gate_up_proj")
+ state_dict[new_key] = gate_up_proj.contiguous()
+
+ tqdm.write(f"Processing: {key.ljust(50)} ->\t {new_key}, {state_dict[new_key].shape}")
+ elif "down_proj" in new_key:
+ current_parameter = torch.cat(current_parameter, dim=concat_dim)
+ if "experts" in new_key:
+ p = []
+ for i in range(8):
+ p += [current_parameter.reshape(8, -1, 5120)[i, :, :].view(num_experts, -1, 5120)]
+ current_parameter = torch.cat(p, dim=1)
+ state_dict[new_key] = current_parameter.contiguous()
+ tqdm.write(f"Processing: {key.ljust(50)} ->\t {new_key}, {state_dict[new_key].shape}")
+ elif "router" in new_key:
+ current_parameter = torch.cat(current_parameter, dim=concat_dim)
+ state_dict[new_key] = current_parameter.transpose(0, 1)
+ elif "lm_head" in new_key:
+ current_parameter = torch.cat(current_parameter, dim=concat_dim).clone()
+ # TODO we need to do better than mean, works for now
+ # if (vocab_size - current_parameter.shape[0]) > 0:
+ # mean_embedding = torch.mean(current_parameter, dim=0)[:, None].repeat(vocab_size-current_parameter.shape[0],1)
+ # print(mean_embedding.shape)
+ # current_parameter = torch.cat((current_parameter, mean_embedding), dim=0)
+ state_dict[new_key] = current_parameter
+ tqdm.write(
+ f"Processing: {key.ljust(50)} ->\t {new_key}, {state_dict[new_key].shape}, concat dim = {concat_dim}"
+ )
+ elif new_key == "vision_model.patch_embedding.linear.weight":
+ current_parameter = torch.cat(current_parameter, dim=concat_dim).clone()
+ # We don't reshape the patch embedding as we're using unfolded convolution as well
+ state_dict[new_key] = current_parameter # .reshape(-1, 3, vision_patch_size, vision_patch_size)
+ # generic concat for weights/select one for biases
+ elif isinstance(current_parameter, list) and len(current_parameter) > 0:
+ if not is_param_same_across_shards(new_key):
+ current_parameter = torch.cat(current_parameter, dim=concat_dim)
+ state_dict[new_key] = current_parameter
+ tqdm.write(
+ f"Processing: {key.ljust(50)} ->\t {new_key}, {state_dict[new_key].shape}, concat dim = {concat_dim}"
+ )
+ elif is_param_same_across_shards(new_key):
+ state_dict[new_key] = current_parameter[0]
+ tqdm.write(
+ f"Processing: {key.ljust(50)} ->\t {new_key}, {state_dict[new_key].shape}, concat dim = {concat_dim}"
+ )
+
+ elif new_key == "":
+ # skip empty keys
+ continue
+ else:
+ # just load the parameter
+ state_dict[new_key] = current_parameter
+ tqdm.write(
+ f"Processing: {key.ljust(50)} ->\t {new_key}, {state_dict[new_key].shape}, concat dim = {concat_dim}"
+ )
+ del loaded
+ gc.collect()
+
+ print("Loading the checkpoint in a Llama4 model.")
+ state_dict.pop("")
+ model.load_state_dict(state_dict, strict=True, assign=True)
+ print("Model reloaded successfully.")
+ print("Saving the model.")
+ model.save_pretrained(model_path)
+ del state_dict, model
+
+ # Safety check: reload the converted model
+ gc.collect()
+ print("Reloading the model to check if it's saved correctly.")
+ with torch.no_grad():
+ # TODO test if we can do `tp_plan="auto"``
+ model = Llama4ForConditionalGeneration.from_pretrained(
+ model_path, dtype=torch.bfloat16, device_map="auto", attn_implementation="eager"
+ )
+
+ model.generation_config.top_p = 0.9
+ model.generation_config.temperature = 0.6
+ print("Model reloaded successfully.")
+
+ from transformers import AutoTokenizer
+
+ tokenizer = AutoTokenizer.from_pretrained(model_path)
+ inputs = tokenizer(["Roses are red,"], return_tensors="pt").to(model.device)
+ out = model.generate(**inputs, max_new_tokens=4)
+ print(tokenizer.batch_decode(out))
+ # generation config
+ if instruct:
+ print("Saving generation config...")
+ generation_config = GenerationConfig(
+ do_sample=True,
+ temperature=0.6,
+ top_p=0.9,
+ bos_token_id=bos_token_id,
+ eos_token_id=eos_token_id,
+ pad_token_id=pad_token_id,
+ )
+ generation_config.save_pretrained(model_path)
+
+
+BOS_ADDED_TOKEN = AddedToken(
+ "<|begin_of_text|>", single_word=False, lstrip=False, rstrip=False, normalized=False, special=True
+)
+EOS_ADDED_TOKEN = AddedToken(
+ "<|end_of_text|>", single_word=False, lstrip=False, rstrip=False, normalized=False, special=True
+)
+EOT_ADDED_TOKEN = AddedToken("<|eot|>", single_word=False, lstrip=False, rstrip=False, normalized=False, special=True)
+
+
+def get_reserved_special_tokens(name, count, start_index=0):
+ return [f"<|{name}_reserved_special_token_{i}|>" for i in range(start_index, start_index + count)]
+
+
+# 200005, ..., 200079
+LLAMA4_TEXT_POST_TRAIN_SPECIAL_TOKENS = [
+ "<|header_start|>",
+ "<|header_end|>",
+ "<|eom|>",
+ "<|eot|>",
+ "<|step|>",
+ "<|text_post_train_reserved_special_token_0|>",
+ "<|text_post_train_reserved_special_token_1|>",
+ "<|text_post_train_reserved_special_token_2|>",
+ "<|text_post_train_reserved_special_token_3|>",
+ "<|text_post_train_reserved_special_token_4|>",
+ "<|text_post_train_reserved_special_token_5|>",
+ "<|python_start|>",
+ "<|python_end|>",
+ "<|finetune_right_pad|>",
+] + get_reserved_special_tokens(
+ "text_post_train", 61, 8
+) # <|text_post_train_reserved_special_token_8|>, ..., <|text_post_train_reserved_special_token_68|>
+
+# 200080, ..., 201133
+LLAMA4_VISION_SPECIAL_TOKENS = [
+ "<|image_start|>",
+ "<|image_end|>",
+ "<|vision_reserved_special_token_0|>",
+ "<|vision_reserved_special_token_1|>",
+ "<|tile_x_separator|>",
+ "<|tile_y_separator|>",
+ "<|vision_reserved_special_token_2|>",
+ "<|vision_reserved_special_token_3|>",
+ "<|vision_reserved_special_token_4|>",
+ "<|vision_reserved_special_token_5|>",
+ "<|image|>",
+ "<|vision_reserved_special_token_6|>",
+ "<|patch|>",
+] + get_reserved_special_tokens(
+ "vision", 1041, 7
+) # <|vision_reserved_special_token_7|>, ..., <|vision_reserved_special_token_1047|>
+
+LLAMA4_SPECIAL_TOKENS = LLAMA4_TEXT_POST_TRAIN_SPECIAL_TOKENS + LLAMA4_VISION_SPECIAL_TOKENS
+
+BASIC_SPECIAL_TOKENS = [
+ "<|begin_of_text|>",
+ "<|end_of_text|>",
+ "<|fim_prefix|>",
+ "<|fim_middle|>",
+ "<|fim_suffix|>",
+]
+
+
+class Llama4Converter(TikTokenConverter):
+ def __init__(
+ self,
+ vocab_file,
+ special_tokens: list[str],
+ pattern: str,
+ model_max_length: int = 0,
+ chat_template: str | None = None,
+ **kwargs,
+ ):
+ super().__init__(vocab_file, pattern=pattern)
+ self.additional_special_tokens = special_tokens
+ tokenizer = self.converted()
+ if chat_template is not None:
+ kwargs["chat_template"] = chat_template
+
+ self.converted_tokenizer = PreTrainedTokenizerFast(
+ tokenizer_object=tokenizer,
+ model_input_names=["input_ids", "attention_mask"],
+ model_max_length=model_max_length,
+ **kwargs,
+ )
+
+ instruct = chat_template is not None
+ self.update_post_processor(self.converted_tokenizer)
+ # finer special_tokens_map.json
+ self.converted_tokenizer._bos_token = BOS_ADDED_TOKEN
+ self.converted_tokenizer._eos_token = EOT_ADDED_TOKEN if instruct else EOS_ADDED_TOKEN
+
+ # We can't do this while building the tokenizer because we have no easy access to the bos token id
+ def update_post_processor(self, tokenizer):
+ tokenizer._tokenizer.post_processor = processors.Sequence(
+ [
+ processors.ByteLevel(trim_offsets=False),
+ processors.TemplateProcessing(
+ single="<|begin_of_text|> $A",
+ pair="<|begin_of_text|>:0 $A:0 <|begin_of_text|>:1 $B:1",
+ special_tokens=[
+ ("<|begin_of_text|>", tokenizer.convert_tokens_to_ids("<|begin_of_text|>")),
+ ],
+ ),
+ ]
+ )
+
+
+O200K_PATTERN = r"""[^\r\n\p{L}\p{N}]?[\p{Lu}\p{Lt}\p{Lm}\p{Lo}\p{M}]*[\p{Ll}\p{Lm}\p{Lo}\p{M}]+(?i:'s|'t|'re|'ve|'m|'ll|'d)?|[^\r\n\p{L}\p{N}]?[\p{Lu}\p{Lt}\p{Lm}\p{Lo}\p{M}]+[\p{Ll}\p{Lm}\p{Lo}\p{M}]*(?i:'s|'t|'re|'ve|'m|'ll|'d)?|\p{N}{1,3}| ?[^\s\p{L}\p{N}]+[\r\n/]*|\s*[\r\n]+|\s+(?!\S)|\s+"""
+
+
+def write_tokenizer(args):
+ tokenizer_path = os.path.join(args.input_dir, "tokenizer.model")
+ chat_template = "{{- bos_token }}\n{%- if custom_tools is defined %}\n {%- set tools = custom_tools %}\n{%- endif %}\n{%- if not tools_in_user_message is defined %}\n {%- set tools_in_user_message = true %}\n{%- endif %}\n{%- if not date_string is defined %}\n {%- if strftime_now is defined %}\n {%- set date_string = strftime_now(\"%d %b %Y\") %}\n {%- else %}\n {%- set date_string = \"26 Jul 2024\" %}\n {%- endif %}\n{%- endif %}\n{%- if not tools is defined %}\n {%- set tools = none %}\n{%- endif %}\n\n{#- This block extracts the system message, so we can slot it into the right place. #}\n{%- if messages[0]['role'] == 'system' %} \n {%- if messages[0]['content'] is string %}\n {%- set system_message = messages[0]['content']|trim %}\n {%- else %}\n {#- FIXME: The processor requires an array, always. #}\n {%- set system_message = messages[0]['content'][0]['text']|trim %}\n {%- endif %}\n {%- set messages = messages[1:] %}\n {%- set user_supplied_system_message = true %}\n{%- else %}\n {%- set system_message = \"\" %}\n {%- set user_supplied_system_message = false %}\n{%- endif %}\n\n{#- System message if the user supplied one #}\n{%- if user_supplied_system_message %}\n {{- \"<|header_start|>system<|header_end|>\n\n\" }}\n {%- if tools is not none %}\n {{- \"Environment: ipython\n\" }}\n {%- endif %}\n {%- if tools is not none and not tools_in_user_message %}\n {{- \"You have access to the following functions. To call a function, please respond with JSON for a function call.\" }}\n {{- 'Respond in the format {\"name\": function name, \"parameters\": dictionary of argument name and its value}.' }}\n {{- \"Do not use variables.\n\n\" }}\n {%- for t in tools %}\n {{- t | tojson(indent=4) }}\n {{- \"\n\n\" }}\n {%- endfor %}\n {%- endif %}\n {{- system_message }}\n {{- \"<|eot|>\" }}\n{%- endif %}\n\n{#- Custom tools are passed in a user message with some extra guidance #}\n{%- if tools_in_user_message and not tools is none %}\n {#- Extract the first user message so we can plug it in here #}\n {%- if messages | length != 0 %}\n {%- set first_user_message = messages[0]['content']|trim %}\n {%- set messages = messages[1:] %}\n {%- else %}\n {{- raise_exception(\"Cannot put tools in the first user message when there's no first user message!\") }}\n{%- endif %}\n {{- '<|header_start|>user<|header_end|>\n\n' -}}\n {{- \"Given the following functions, please respond with a JSON for a function call \" }}\n {{- \"with its proper arguments that best answers the given prompt.\n\n\" }}\n {{- 'Respond in the format {\"name\": function name, \"parameters\": dictionary of argument name and its value}.' }}\n {{- \"Do not use variables.\n\n\" }}\n {%- for t in tools %}\n {{- t | tojson(indent=4) }}\n {{- \"\n\n\" }}\n {%- endfor %}\n {{- first_user_message + \"<|eot|>\"}}\n{%- endif %}\n\n{%- for message in messages %}\n {%- if not (message.role == 'ipython' or message.role == 'tool' or 'tool_calls' in message) %}\n {{- '<|header_start|>' + message['role'] + '<|header_end|>\n\n' }}\n {%- if message['content'] is string %}\n {{- message['content'] }}\n {%- else %}\n {%- for content in message['content'] %}\n {%- if content['type'] == 'image' %}\n {{- '<|image|>' }}\n {%- elif content['type'] == 'text' %}\n {{- content['text'] }}\n {%- endif %}\n {%- endfor %}\n {%- endif %}\n {{- \"<|eot|>\" }}\n {%- elif 'tool_calls' in message and message.tool_calls|length > 0 %}\n {{- '<|header_start|>assistant<|header_end|>\n\n' -}}\n {{- '<|python_start|>' }}\n {%- if message['content'] is string %}\n {{- message['content'] }}\n {%- else %}\n {%- for content in message['content'] %}\n {%- if content['type'] == 'image' %}\n {{- '<|image|>' }}\n {%- elif content['type'] == 'text' %}\n {{- content['text'] }}\n {%- endif %}\n {%- endfor %}\n {%- endif %}\n {{- '<|python_end|>' }}\n {%- for tool_call in message.tool_calls %}\n {{- '{\"name\": \"' + tool_call.function.name + '\", ' }}\n {{- '\"parameters\": ' }}\n {{- tool_call.function.arguments | tojson }}\n {{- \"}\" }}\n {%- endfor %}\n {{- \"<|eot|>\" }}\n {%- elif message.role == \"tool\" or message.role == \"ipython\" %}\n {{- \"<|header_start|>ipython<|header_end|>\n\n\" }}\n {%- if message.content is mapping or message.content is iterable %}\n {{- message.content | tojson }}\n {%- else %}\n {{- message.content }}\n {%- endif %}\n {{- \"<|eot|>\" }}\n {%- endif %}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|header_start|>assistant<|header_end|>\n\n' }}\n{%- endif %}\n"
+
+ special_tokens = BASIC_SPECIAL_TOKENS + LLAMA4_SPECIAL_TOKENS
+ converter = Llama4Converter(
+ vocab_file=tokenizer_path,
+ pattern=O200K_PATTERN,
+ special_tokens=special_tokens,
+ chat_template=chat_template if args.instruct else None,
+ bos_token="<|begin_of_text|>",
+ eos_token="<|end_of_text|>" if not args.instruct else "<|eot|>",
+ pad_token="<|finetune_right_pad_id|>",
+ model_max_length=max_context_length(args.input_dir, args.instruct),
+ )
+ tokenizer = converter.converted_tokenizer
+
+ image_processor = Llama4ImageProcessorFast()
+ processor = Llama4Processor(
+ image_processor=image_processor,
+ tokenizer=tokenizer,
+ chat_template=tokenizer.chat_template,
+ )
+ processor.save_pretrained(args.output_dir)
+ del processor
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "--input_dir",
+ type=str,
+ help="Location of the local folder copied from the Hub.",
+ )
+ parser.add_argument(
+ "--output_dir",
+ type=str,
+ help="Location to write HF model and tokenizer",
+ )
+ parser.add_argument(
+ "--special_tokens",
+ default=None,
+ type=list[str],
+ help="The list of special tokens that should be added to the model.",
+ )
+ parser.add_argument(
+ "--num_shards",
+ default=8,
+ type=int,
+ help="The number of individual shards used for the model. Does not have to be the same as the number of consolidated_xx.pth",
+ )
+ parser.add_argument(
+ "--instruct",
+ action="store_true",
+ help="Whether the model is an instruct model",
+ )
+ parser.add_argument(
+ "--convert_checkpoints",
+ action="store_true",
+ help="Whether to convert the original weights (or skip if previously converted)",
+ )
+
+ args = parser.parse_args()
+ write_tokenizer(args)
+
+ write_model(
+ model_path=args.output_dir,
+ input_base_path=args.input_dir,
+ num_shards=args.num_shards,
+ instruct=args.instruct,
+ convert_checkpoints=args.convert_checkpoints,
+ )
diff --git a/third_party/transformers/src/transformers/models/llama4/image_processing_llama4.py b/third_party/transformers/src/transformers/models/llama4/image_processing_llama4.py
new file mode 100644
index 0000000000000000000000000000000000000000..1c055427251d5ba19ce841ce69e76f8ca5bb8047
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/llama4/image_processing_llama4.py
@@ -0,0 +1,425 @@
+# Copyright 2025 HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Image processor class for Llama4."""
+
+import math
+from collections import defaultdict
+from functools import lru_cache
+
+import torch
+from torchvision.transforms.v2 import functional as tvF
+
+from ...image_processing_backends import TorchvisionBackend
+from ...image_processing_utils import BatchFeature
+from ...image_transforms import group_images_by_shape, reorder_images, split_to_tiles
+from ...image_utils import ImageInput, PILImageResampling, SizeDict
+from ...processing_utils import ImagesKwargs, Unpack
+from ...utils import TensorType, auto_docstring
+
+
+def get_factors(dividend: int) -> set[int]:
+ """
+ Calculate all factors of a given number, i.e. a divisor that leaves
+ no remainder. For example, if dividend=12, it will return {1, 2, 3, 4, 6, 12}.
+
+ Args:
+ dividend (int): The number to find factors for.
+
+ Returns:
+ set: A set containing all factors of the number.
+ """
+ factors_set = set()
+
+ for i in range(1, int(dividend**0.5) + 1):
+ if dividend % i == 0:
+ factors_set.add(i)
+ factors_set.add(dividend // i)
+ return factors_set
+
+
+def get_max_res_without_distortion(
+ image_size: tuple[int, int],
+ target_size: tuple[int, int],
+) -> tuple[int, int]:
+ """
+ Determines the maximum resolution to which an image can be resized to without distorting its
+ aspect ratio, based on the target resolution.
+
+ Args:
+ image_size (tuple[int, int]): The original resolution of the image (height, width).
+ target_resolution (tuple[int, int]): The desired resolution to fit the image into (height, width).
+ Returns:
+ tuple[int, int]: The optimal dimensions (height, width) to which the image should be resized.
+ Example:
+ >>> _get_max_res_without_distortion([200, 300], target_size = [450, 200])
+ (134, 200)
+ >>> _get_max_res_without_distortion([800, 600], target_size = [450, 1300])
+ (450, 338)
+ """
+
+ original_height, original_width = image_size
+ target_height, target_width = target_size
+
+ scale_w = target_width / original_width
+ scale_h = target_height / original_height
+
+ if scale_w < scale_h:
+ new_width = target_width
+ new_height = min(math.floor(original_height * scale_w), target_height)
+ else:
+ new_height = target_height
+ new_width = min(math.floor(original_width * scale_h), target_width)
+
+ return new_height, new_width
+
+
+@lru_cache(maxsize=1)
+def find_supported_resolutions(max_num_chunks: int, patch_size: SizeDict) -> torch.Tensor:
+ """
+ Computes all of the allowed resolutions for a fixed number of chunks
+ and patch_size. Useful for when dividing an image into chunks.
+
+ Args:
+ max_num_chunks (int): Maximum number of chunks for processing.
+ patch_size (int): Size of the side of the patch.
+
+ Returns:
+ torch.Tensor: List of possible resolutions as tuples (height, width).
+
+ Example:
+ >>> max_num_chunks = 5
+ >>> patch_size = 224
+ >>> find_supported_resolutions(max_num_chunks, patch_size)
+ tensor([(224, 896), (448, 448), (224, 224), (896, 224), (224, 672),
+ (672, 224), (224, 448), (448, 224)])
+
+ Given max_num_chunks=4, patch_size=224, it will create a dictionary:
+ {
+ 0.25: [(1, 4)],
+ 1.0: [(2, 2), (1, 1)],
+ 4.0: [(4, 1)],
+ 0.33: [(1, 3)],
+ 3.0: [(3, 1)],
+ 0.5: [(1, 2)],
+ 2.0: [(2, 1)]
+ }
+
+ and return the resolutions multiplied by the patch_size:
+ [(1*224, 4*224), (2*224, 2*224), ..., (2*224, 1*224)]
+ """
+ height, width = patch_size.height, patch_size.width
+ if height != width:
+ raise ValueError("`size` must be square.")
+
+ patch_size = height
+
+ asp_dict = defaultdict(list)
+ for chunk_size in range(max_num_chunks, 0, -1):
+ _factors = sorted(get_factors(chunk_size))
+ _asp_ratios = [(factor, chunk_size // factor) for factor in _factors]
+ for height, width in _asp_ratios:
+ ratio_float = height / width
+ asp_dict[ratio_float].append((height, width))
+
+ # get the resolutions multiplied by the patch_size
+ possible_resolutions = []
+ for value in asp_dict.values():
+ for height, depth in value:
+ possible_resolutions.append((height * patch_size, depth * patch_size))
+
+ return possible_resolutions
+
+
+def pad_to_best_fit(
+ images: "torch.Tensor",
+ target_size: tuple[int, int],
+ background_color: int | tuple[int, int, int] = 0,
+) -> "torch.Tensor":
+ """
+ Pads an image to fit the target size.
+
+ Args:
+ images (`np.ndarray`):
+ The images to pad.
+ background_color (`int` or `tuple[int, int, int]`, *optional*, defaults to 0):
+ The color to use for the padding. Can be an integer for single channel or a
+ tuple of integers representing for multi-channel images. If passed as integer
+ in multi-channel mode, it will default to `0` in subsequent channels.
+ Returns:
+ `torch.Tensor`: The padded images.
+ """
+
+ num_channels = images.shape[1] if len(images.shape) == 4 else images.shape[0]
+ if isinstance(background_color, int):
+ background_color = [background_color] + [0] * (num_channels - 1)
+ elif len(background_color) != num_channels:
+ raise ValueError(
+ f"background_color must have no more than {num_channels} elements to match the number of channels"
+ )
+
+ height, width = images.shape[-2:]
+ target_height, target_width = target_size
+ paste_x_right = target_width - width
+ paste_y_right = target_height - height
+ padded_images = tvF.pad(images, padding=[0, 0, paste_x_right, paste_y_right], fill=background_color)
+
+ return padded_images
+
+
+def get_best_fit(
+ image_size: tuple[int, int],
+ possible_resolutions: torch.Tensor,
+ resize_to_max_canvas: bool = False,
+) -> tuple[int, int]:
+ """
+ Determines the best canvas possible from a list of possible resolutions to, without distortion,
+ resize an image to.
+
+ For each possible resolution, calculates the scaling factors for
+ width and height, and selects the smallest one, which is the limiting side.
+ E.g. to match the canvas you can upscale height by 2x, and width by 1.5x,
+ therefore, the maximum upscaling you can do is min(2, 1.5) = 1.5.
+
+ If upscaling is possible (any of the scaling factors is greater than 1),
+ then picks the smallest upscaling factor > 1, unless resize_to_max_canvas is True.
+
+ If upscaling is not possible, then picks the largest scaling factor <= 1, i.e.
+ reduce downscaling as much as possible.
+
+ If there are multiple resolutions with the same max scale, we pick the one with the lowest area,
+ to minimize padding. E.g., the same image can be upscaled to 224x224 and 224x448, but the latter
+ has more padding.
+
+ Args:
+ image_size (tuple[int, int]): A tuple containing the height and width of the image.
+ possible_resolutions (torch.Tensor): A tensor of shape (N, 2) where each
+ row represents a possible resolution (height, width).
+ resize_to_max_canvas (bool): If True, will return the largest upscaling resolution.
+
+ Returns:
+ list[int]: The best resolution [height, width] for the given image.
+
+ Example:
+ >>> image_size = (200, 300)
+ >>> possible_resolutions = torch.tensor([[224, 672],
+ ... [672, 224],
+ ... [224, 448],
+ ... [448, 224],
+ ... [224, 224]])
+ >>> get_best_fit(image_size, possible_resolutions)
+ [224, 448]
+
+ We have:
+ scale_w = tensor([2.2400, 0.7467, 1.4933, 0.7467, 0.7467])
+ scale_h = tensor([1.1200, 3.3600, 1.1200, 2.2400, 1.1200])
+ scales = tensor([1.1200, 0.7467, 1.1200, 0.7467, 0.7467])
+ Only one of the scales > 1:
+ upscaling_possible = tensor([1.1200, 1.1200])
+ smallest_rescale = tensor(1.1200)
+ So we pick the resolution with the smallest smallest area:
+ areas = tensor([150528, 100352]) # [672, 224], [224, 448]
+ optimal_canvas = tensor([224, 448])
+ """
+
+ original_height, original_width = image_size
+
+ # get all possible resolutions heights/widths
+ target_heights, target_widths = (
+ possible_resolutions[:, 0],
+ possible_resolutions[:, 1],
+ )
+
+ # get scaling factors to resize the image without distortion
+ scale_w = target_widths / original_width
+ scale_h = target_heights / original_height
+
+ # get the min scale between width and height (limiting side -> no distortion)
+ scales = torch.where(scale_h > scale_w, scale_w, scale_h)
+
+ # filter only scales that allow upscaling
+ upscaling_options = scales[scales >= 1]
+ if len(upscaling_options) > 0:
+ if resize_to_max_canvas:
+ selected_scale = torch.max(upscaling_options)
+ else:
+ selected_scale = torch.min(upscaling_options)
+ else:
+ # no upscaling possible,
+ # get the minimum downscaling (max scale for scales<1)
+ downscaling_options = scales[scales < 1]
+ selected_scale = torch.max(downscaling_options)
+
+ # get all resolutions that support this scaling factor,
+ # e.g. you can upscale to 224x224, 224x448, 224x672 without distortion
+ chosen_canvas = possible_resolutions[scales == selected_scale]
+
+ # if there are multiple resolutions,
+ # get the one with minimum area to reduce padding
+ if len(chosen_canvas) > 1:
+ areas = chosen_canvas[:, 0] * chosen_canvas[:, 1]
+ optimal_idx = torch.argmin(areas)
+ optimal_canvas = chosen_canvas[optimal_idx]
+ else:
+ optimal_canvas = chosen_canvas[0]
+
+ return optimal_canvas
+
+
+class Llama4ImageProcessorKwargs(ImagesKwargs, total=False):
+ r"""
+ max_patches (`int`, *optional*, defaults to 16):
+ The maximum number of patches to be extracted from the image.
+ Can be overridden by the `max_patches` parameter in the `preprocess` method.
+ resize_to_max_canvas (`bool`, *optional*, defaults to False):
+ Whether to resize the image to the maximum canvas size.
+ If True, picks the canvas the allows the largest resizing without distortion.
+ If False, downsample as little as possible, including no resizing at all,
+ but never upsample, unless the image is smaller than the patch size.
+ """
+
+ max_patches: int
+ resize_to_max_canvas: bool
+
+
+@auto_docstring
+class Llama4ImageProcessor(TorchvisionBackend):
+ resample = PILImageResampling.BILINEAR
+ image_mean = [0.5, 0.5, 0.5]
+ image_std = [0.5, 0.5, 0.5]
+ size = {"height": 336, "width": 336}
+ do_resize = True
+ do_rescale = True
+ do_normalize = True
+ do_convert_rgb = True
+ max_patches = 16
+ resize_to_max_canvas = False
+ valid_kwargs = Llama4ImageProcessorKwargs
+
+ def __init__(self, **kwargs: Unpack[Llama4ImageProcessorKwargs]):
+ super().__init__(**kwargs)
+
+ @auto_docstring
+ def preprocess(self, images: ImageInput, **kwargs: Unpack[Llama4ImageProcessorKwargs]) -> BatchFeature:
+ return super().preprocess(images, **kwargs)
+
+ # Disable compilation here as conversion to bfloat16 causes differences in the output of the compiled and non-compiled versions
+ @torch.compiler.disable
+ def rescale_and_normalize(
+ self,
+ images: "torch.Tensor",
+ do_rescale: bool,
+ rescale_factor: float,
+ do_normalize: bool,
+ image_mean: float | list[float],
+ image_std: float | list[float],
+ ) -> "torch.Tensor":
+ """
+ Rescale and normalize images.
+ Override to rescale and normalize the images in torch.bfloat16 as in the original implementation
+ """
+ if do_rescale and do_normalize:
+ images = images.to(dtype=torch.bfloat16) * rescale_factor
+ images = self.normalize(images, image_mean, image_std)
+ elif do_rescale:
+ images = images * rescale_factor
+ elif do_normalize:
+ images = self.normalize(images, image_mean, image_std)
+
+ return images
+
+ def _preprocess(
+ self,
+ images: list["torch.Tensor"],
+ do_resize: bool,
+ size: SizeDict,
+ resample: "PILImageResampling | tvF.InterpolationMode | int | None",
+ do_rescale: bool,
+ rescale_factor: float,
+ do_normalize: bool,
+ image_mean: float | list[float] | None,
+ image_std: float | list[float] | None,
+ max_patches: int,
+ resize_to_max_canvas: bool,
+ disable_grouping: bool | None,
+ return_tensors: str | TensorType | None,
+ **kwargs,
+ ) -> BatchFeature:
+ possible_resolutions = find_supported_resolutions(max_num_chunks=max_patches, patch_size=size)
+ possible_resolutions = torch.tensor(possible_resolutions, device=images[0].device)
+ # process images by batch, grouped by shape
+ grouped_images, grouped_images_index = group_images_by_shape(images, disable_grouping=disable_grouping)
+ grouped_processed_images = {}
+ grouped_aspect_ratios = {}
+ for shape, stacked_images in grouped_images.items():
+ image_size = stacked_images.shape[-2:]
+ target_size = get_best_fit(image_size, possible_resolutions, resize_to_max_canvas=resize_to_max_canvas)
+ # If target_size requires upscaling, we might want to limit the upscaling to max_upscaling_size
+ max_upscaling_size = None if resize_to_max_canvas else size.height
+ if max_upscaling_size is not None:
+ new_target_height = min(max(image_size[0], max_upscaling_size), target_size[0])
+ new_target_width = min(max(image_size[1], max_upscaling_size), target_size[1])
+ target_size_without_distortion = (new_target_height, new_target_width)
+ else:
+ target_size_without_distortion = target_size
+
+ # resize to target_size while preserving aspect ratio
+ new_size_without_distortion = get_max_res_without_distortion(image_size, target_size_without_distortion)
+ new_size_without_distortion = SizeDict(
+ height=max(new_size_without_distortion[0], 1), width=max(new_size_without_distortion[1], 1)
+ )
+
+ processed_images = self.resize(
+ stacked_images,
+ new_size_without_distortion,
+ resample=resample,
+ )
+
+ # pad to target_size to be able to split into tiles
+ processed_images = pad_to_best_fit(processed_images, target_size)
+ processed_images = self.rescale_and_normalize(
+ processed_images, do_rescale, rescale_factor, do_normalize, image_mean, image_std
+ )
+
+ ratio_h, ratio_w = (
+ target_size[0] // size.height,
+ target_size[1] // size.width,
+ )
+ # split into tiles
+ processed_images = split_to_tiles(processed_images, ratio_h, ratio_w)
+ grouped_processed_images[shape] = processed_images
+ grouped_aspect_ratios[shape] = torch.tensor(
+ [[ratio_h, ratio_w]] * stacked_images.shape[0], device=images[0].device
+ )
+
+ # add a global tile to the processed tile if there are more than one tile
+ if ratio_h * ratio_w > 1:
+ global_tiles = self.resize(
+ stacked_images,
+ size,
+ resample=resample,
+ )
+ global_tiles = self.rescale_and_normalize(
+ global_tiles, do_rescale, rescale_factor, do_normalize, image_mean, image_std
+ )
+ grouped_processed_images[shape] = torch.cat([processed_images, global_tiles.unsqueeze(1)], dim=1)
+ processed_images = reorder_images(grouped_processed_images, grouped_images_index)
+ aspect_ratios = reorder_images(grouped_aspect_ratios, grouped_images_index)
+
+ processed_images = torch.cat(processed_images, dim=0) if return_tensors else processed_images
+ return BatchFeature(
+ data={"pixel_values": processed_images, "aspect_ratios": aspect_ratios}, tensor_type=return_tensors
+ )
+
+
+__all__ = ["Llama4ImageProcessor"]
diff --git a/third_party/transformers/src/transformers/models/llama4/modeling_llama4.py b/third_party/transformers/src/transformers/models/llama4/modeling_llama4.py
new file mode 100644
index 0000000000000000000000000000000000000000..08d50bd63f72a0eaf923ba16cc267bcc2a56ec44
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/llama4/modeling_llama4.py
@@ -0,0 +1,1418 @@
+# Copyright 2025 The LLAMA4 and HuggingFace Inc. team. All rights reserved.
+#
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import math
+from collections.abc import Callable
+from dataclasses import dataclass
+from typing import Optional
+
+import torch
+import torch.nn as nn
+import torch.nn.functional as F
+
+from transformers.models.llama4.configuration_llama4 import Llama4VisionConfig
+
+from ... import initialization as init
+from ...activations import ACT2FN
+from ...cache_utils import Cache, DynamicCache
+from ...generation import GenerationMixin
+from ...integrations import use_kernel_forward_from_hub
+from ...masking_utils import create_causal_mask, create_chunked_causal_mask
+from ...modeling_flash_attention_utils import FlashAttentionKwargs
+from ...modeling_layers import GradientCheckpointingLayer
+from ...modeling_outputs import (
+ BaseModelOutput,
+ BaseModelOutputWithPast,
+ BaseModelOutputWithPooling,
+ CausalLMOutputWithPast,
+ ModelOutput,
+)
+from ...modeling_rope_utils import (
+ ROPE_INIT_FUNCTIONS,
+ dynamic_rope_update,
+)
+from ...modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
+from ...processing_utils import Unpack
+from ...utils import TransformersKwargs, auto_docstring, can_return_tuple, logging, torch_compilable_check
+from ...utils.generic import maybe_autocast, merge_with_config_defaults
+from ...utils.output_capturing import capture_outputs
+from .configuration_llama4 import Llama4Config, Llama4TextConfig
+
+
+logger = logging.get_logger(__name__)
+
+
+class Llama4TextExperts(nn.Module):
+ def __init__(self, config: Llama4TextConfig):
+ super().__init__()
+ self.num_experts = config.num_local_experts
+ self.intermediate_size = config.intermediate_size
+ self.hidden_size = config.hidden_size
+ self.expert_dim = self.intermediate_size
+ self.gate_up_proj = nn.Parameter(torch.zeros(self.num_experts, self.hidden_size, 2 * self.expert_dim))
+ self.down_proj = nn.Parameter(torch.empty((self.num_experts, self.expert_dim, self.hidden_size)))
+ self.act_fn = ACT2FN[config.hidden_act]
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ """
+ This should really not be run on a single machine, as we are reaching compute bound:
+ - the inputs are expected to be "sorted" per expert already.
+ - the weights are viewed with another dim, to match num_expert, 1, shape * num_tokens, shape
+
+ Args:
+ hidden_states (torch.Tensor): (batch_size * token_num, hidden_size)
+ selected_experts (torch.Tensor): (batch_size * token_num, top_k)
+ routing_weights (torch.Tensor): (batch_size * token_num, top_k)
+ Returns:
+ torch.Tensor
+ """
+ hidden_states = hidden_states.view(self.gate_up_proj.shape[0], -1, self.hidden_size)
+ gate_up = torch.bmm(hidden_states, self.gate_up_proj)
+ gate, up = gate_up.chunk(2, dim=-1) # not supported for DTensors
+ next_states = torch.bmm((up * self.act_fn(gate)), self.down_proj)
+ next_states = next_states.view(-1, self.hidden_size)
+ return next_states
+
+
+# Phi3MLP
+class Llama4TextMLP(nn.Module):
+ def __init__(self, config, intermediate_size=None):
+ super().__init__()
+
+ if intermediate_size is None:
+ intermediate_size = config.intermediate_size
+
+ self.config = config
+ self.gate_proj = nn.Linear(config.hidden_size, intermediate_size, bias=False)
+ self.up_proj = nn.Linear(config.hidden_size, intermediate_size, bias=False)
+ self.down_proj = nn.Linear(intermediate_size, config.hidden_size, bias=False)
+ self.activation_fn = ACT2FN[config.hidden_act]
+
+ def forward(self, x):
+ down_proj = self.activation_fn(self.gate_proj(x)) * self.up_proj(x)
+ return self.down_proj(down_proj)
+
+
+class Llama4TextL2Norm(torch.nn.Module):
+ def __init__(self, eps: float = 1e-6):
+ super().__init__()
+ self.eps = eps
+
+ def _norm(self, x):
+ return x * torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + self.eps)
+
+ def forward(self, x):
+ return self._norm(x.float()).type_as(x)
+
+ def extra_repr(self):
+ return f"eps={self.eps}"
+
+
+class Llama4TextRMSNorm(nn.Module):
+ def __init__(self, hidden_size, eps=1e-5):
+ """
+ Llama4RMSNorm is equivalent to T5LayerNorm
+ """
+ super().__init__()
+ self.eps = eps
+ self.weight = nn.Parameter(torch.ones(hidden_size))
+
+ def _norm(self, x):
+ return x * torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + self.eps)
+
+ def forward(self, x):
+ output = self._norm(x.float()).type_as(x)
+ return output * self.weight
+
+ def extra_repr(self):
+ return f"{tuple(self.weight.shape)}, eps={self.eps}"
+
+
+class Llama4Router(nn.Linear):
+ def __init__(self, config):
+ super().__init__(config.hidden_size, config.num_local_experts, bias=False)
+ self.num_experts = config.num_local_experts
+ self.top_k = config.num_experts_per_tok
+
+ def forward(self, hidden_states):
+ router_logits = super().forward(hidden_states)
+ router_top_value, router_indices = torch.topk(router_logits, self.top_k, dim=1)
+ router_scores = torch.full_like(router_logits, float("-inf")).scatter_(1, router_indices, router_top_value)
+ router_scores = torch.nn.functional.sigmoid(router_scores.float()).to(router_scores.dtype)
+ return router_scores, router_logits
+
+
+@use_kernel_forward_from_hub("Llama4TextMoe")
+class Llama4TextMoe(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+ self.top_k = config.num_experts_per_tok
+ self.hidden_dim = config.hidden_size
+ self.num_experts = config.num_local_experts
+ self.experts = Llama4TextExperts(config)
+ self.router = Llama4Router(config)
+ self.shared_expert = Llama4TextMLP(config)
+
+ def forward(self, hidden_states):
+ hidden_states = hidden_states.reshape(-1, self.hidden_dim)
+ router_scores, router_logits = self.router(hidden_states)
+ routed_in = hidden_states.repeat(router_scores.shape[1], 1)
+ routed_in = routed_in * router_scores.transpose(0, 1).reshape(-1, 1)
+ routed_out = self.experts(routed_in)
+ out = self.shared_expert(hidden_states)
+ out.add_(routed_out.reshape(router_scores.shape[1], -1, routed_out.shape[-1]).sum(dim=0))
+ return out, router_logits
+
+
+# Copied from transformers.models.llama.modeling_llama.LlamaRotaryEmbedding with Llama->Llama4Text
+class Llama4TextRotaryEmbedding(nn.Module):
+ inv_freq: torch.Tensor # fix linting for `register_buffer`
+
+ # Ignore copy
+ def __init__(self, config: Llama4TextConfig, device=None):
+ super().__init__()
+ self.max_seq_len_cached = config.max_position_embeddings
+ self.original_max_seq_len = config.max_position_embeddings
+
+ self.config = config
+
+ self.rope_type = self.config.rope_parameters["rope_type"]
+ rope_init_fn: Callable = self.compute_default_rope_parameters
+ if self.rope_type != "default":
+ rope_init_fn = ROPE_INIT_FUNCTIONS[self.rope_type]
+ inv_freq, self.attention_scaling = rope_init_fn(self.config, device)
+
+ self.register_buffer("inv_freq", inv_freq, persistent=False)
+ self.register_buffer("original_inv_freq", inv_freq.clone(), persistent=False)
+
+ @staticmethod
+ def compute_default_rope_parameters(
+ config: Llama4TextConfig | None = None,
+ device: Optional["torch.device"] = None,
+ seq_len: int | None = None,
+ ) -> tuple["torch.Tensor", float]:
+ """
+ Computes the inverse frequencies according to the original RoPE implementation
+ Args:
+ config ([`~transformers.PreTrainedConfig`]):
+ The model configuration.
+ device (`torch.device`):
+ The device to use for initialization of the inverse frequencies.
+ seq_len (`int`, *optional*):
+ The current sequence length. Unused for this type of RoPE.
+ Returns:
+ Tuple of (`torch.Tensor`, `float`), containing the inverse frequencies for the RoPE embeddings and the
+ post-processing scaling factor applied to the computed cos/sin (unused in this type of RoPE).
+ """
+ base = config.rope_parameters["rope_theta"]
+ dim = getattr(config, "head_dim", None) or config.hidden_size // config.num_attention_heads
+
+ attention_factor = 1.0 # Unused in this type of RoPE
+
+ # Compute the inverse frequencies
+ inv_freq = 1.0 / (
+ base ** (torch.arange(0, dim, 2, dtype=torch.int64).to(device=device, dtype=torch.float) / dim)
+ )
+ return inv_freq, attention_factor
+
+ # Ignore copy
+ @torch.no_grad()
+ @dynamic_rope_update # power user: used with advanced RoPE types (e.g. dynamic rope)
+ def forward(self, x, position_ids):
+ inv_freq_expanded = self.inv_freq[None, :, None].float().expand(position_ids.shape[0], -1, 1)
+ position_ids_expanded = position_ids[:, None, :].float()
+
+ device_type = x.device.type if isinstance(x.device.type, str) and x.device.type != "mps" else "cpu"
+ with maybe_autocast(device_type=device_type, enabled=False): # Force float32
+ freqs = (inv_freq_expanded.to(x.device) @ position_ids_expanded).transpose(1, 2)
+ freqs_cis = torch.polar(torch.ones_like(freqs), freqs) # Convert to complex representation
+ freqs_cis = freqs_cis * self.attention_scaling
+
+ return freqs_cis
+
+
+def apply_rotary_emb(
+ xq: torch.Tensor,
+ xk: torch.Tensor,
+ freqs_cis: torch.Tensor,
+) -> tuple[torch.Tensor, torch.Tensor]:
+ xq_ = torch.view_as_complex(xq.float().reshape(*xq.shape[:-1], -1, 2))
+ xk_ = torch.view_as_complex(xk.float().reshape(*xk.shape[:-1], -1, 2))
+ xq_out = torch.view_as_real(xq_ * freqs_cis[:, :, None, :]).flatten(3)
+ xk_out = torch.view_as_real(xk_ * freqs_cis[:, :, None, :]).flatten(3)
+ return xq_out.type_as(xq), xk_out.type_as(xk)
+
+
+def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
+ """
+ This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
+ num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
+ """
+ batch, num_key_value_heads, slen, head_dim = hidden_states.shape
+ if n_rep == 1:
+ return hidden_states
+ hidden_states = hidden_states[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim)
+ return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
+
+
+# Adapted from transformers.models.llama.modeling_llama.eager_attention_forward -> llama4 doesn't cast attn weights to fp32
+def eager_attention_forward(
+ module: nn.Module,
+ query: torch.Tensor,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ attention_mask: torch.Tensor | None,
+ scaling: float,
+ dropout: float = 0.0,
+ **kwargs,
+):
+ key_states = repeat_kv(key, module.num_key_value_groups)
+ value_states = repeat_kv(value, module.num_key_value_groups)
+
+ attn_weights = torch.matmul(query, key_states.transpose(2, 3)) * scaling
+ if attention_mask is not None:
+ attn_weights = attn_weights + attention_mask
+
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1)
+ attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
+ attn_output = torch.matmul(attn_weights, value_states)
+ attn_output = attn_output.transpose(1, 2).contiguous()
+
+ return attn_output, attn_weights
+
+
+# Adapted from transformers.models.llama.modeling_llama.eager_attention_forward -> llama4 doesn't cast attn weights to fp32
+def vision_eager_attention_forward(
+ module: nn.Module,
+ query: torch.Tensor,
+ key: torch.Tensor,
+ value: torch.Tensor,
+ attention_mask: torch.Tensor | None,
+ scaling: float,
+ dropout: float = 0.0,
+ **kwargs,
+):
+ key_states = repeat_kv(key, module.num_key_value_groups)
+ value_states = repeat_kv(value, module.num_key_value_groups)
+
+ attn_weights = torch.matmul(query, key_states.transpose(2, 3)) * module.head_dim**-0.5
+ if attention_mask is not None:
+ attn_weights = attn_weights + attention_mask
+
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1)
+ attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
+ attn_output = torch.matmul(attn_weights, value_states)
+ attn_output = attn_output.transpose(1, 2).contiguous()
+
+ return attn_output, attn_weights
+
+
+class Llama4TextAttention(nn.Module):
+ """Multi-headed attention from 'Attention Is All You Need' paper"""
+
+ def __init__(self, config: Llama4TextConfig, layer_idx):
+ super().__init__()
+ self.config = config
+ self.layer_idx = layer_idx
+ self.head_dim = getattr(config, "head_dim", config.hidden_size // config.num_attention_heads)
+ self.num_attention_heads = config.num_attention_heads
+ self.num_key_value_groups = config.num_attention_heads // config.num_key_value_heads
+ self.num_key_value_heads = config.num_key_value_heads
+ self.scaling = self.head_dim**-0.5
+ self.attn_scale = config.attn_scale
+ self.floor_scale = config.floor_scale
+ self.attn_temperature_tuning = config.attn_temperature_tuning
+ self.attention_dropout = config.attention_dropout
+ self.is_causal = True
+ self.use_rope = config.no_rope_layers[layer_idx]
+ self.q_proj = nn.Linear(
+ config.hidden_size, config.num_attention_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.k_proj = nn.Linear(
+ config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.v_proj = nn.Linear(
+ config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias
+ )
+ self.o_proj = nn.Linear(
+ config.num_attention_heads * self.head_dim, config.hidden_size, bias=config.attention_bias
+ )
+ if self.config.use_qk_norm and self.use_rope:
+ self.qk_norm = Llama4TextL2Norm(config.rms_norm_eps)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor],
+ attention_mask: torch.Tensor | None,
+ past_key_values: Cache | None = None,
+ **kwargs: Unpack[FlashAttentionKwargs],
+ ) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
+ input_shape = hidden_states.shape[:-1]
+ hidden_shape = (*input_shape, -1, self.head_dim)
+
+ query_states = self.q_proj(hidden_states).view(hidden_shape)
+ key_states = self.k_proj(hidden_states).view(*input_shape, -1, self.head_dim)
+ value_states = self.v_proj(hidden_states).view(hidden_shape).transpose(1, 2)
+
+ if self.use_rope: # the 16E model skips rope for long context on certain layers
+ query_states, key_states = apply_rotary_emb(
+ query_states, key_states, position_embeddings.to(query_states.device)
+ )
+
+ if hasattr(self, "qk_norm"): # the 128E model does not use qk_norm
+ query_states = self.qk_norm(query_states)
+ key_states = self.qk_norm(key_states)
+
+ # Use temperature tuning from https://huggingface.co/papers/2501.19399) to NoROPE layers
+ if self.attn_temperature_tuning and not self.use_rope:
+ past_seen_tokens = past_key_values.get_seq_length(self.layer_idx) if past_key_values is not None else 0
+ positions = torch.arange(hidden_states.shape[1], device=hidden_states.device) + past_seen_tokens
+ attn_scales = (
+ torch.log1p(torch.floor((positions.float() + 1.0) / self.floor_scale)) * self.attn_scale + 1.0
+ )
+ attn_scales = attn_scales.view((1, input_shape[-1], 1, 1)).expand((*input_shape, 1, 1)) # batch size > 1
+ query_states = (query_states * attn_scales).to(query_states.dtype)
+
+ query_states = query_states.transpose(1, 2)
+ key_states = key_states.transpose(1, 2)
+
+ if past_key_values is not None:
+ key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx)
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, eager_attention_forward
+ )
+ attn_output, attn_weights = attention_interface(
+ self,
+ query_states,
+ key_states,
+ value_states,
+ attention_mask,
+ dropout=0.0 if not self.training else self.attention_dropout,
+ scaling=self.scaling,
+ **kwargs,
+ )
+
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
+ attn_output = self.o_proj(attn_output)
+ return attn_output, attn_weights
+
+
+class Llama4TextDecoderLayer(GradientCheckpointingLayer):
+ def __init__(self, config, layer_idx):
+ super().__init__()
+ self.hidden_size = config.hidden_size
+ self.layer_idx = layer_idx
+ self.self_attn = Llama4TextAttention(config, layer_idx)
+ self.is_moe_layer = layer_idx in config.moe_layers
+ if self.is_moe_layer: # the 128E model interleaves dense / sparse
+ self.feed_forward = Llama4TextMoe(config)
+ else:
+ self.feed_forward = Llama4TextMLP(config, intermediate_size=config.intermediate_size_mlp)
+
+ self.input_layernorm = Llama4TextRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.post_attention_layernorm = Llama4TextRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ use_cache: bool | None = False,
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
+ **kwargs: Unpack[FlashAttentionKwargs],
+ ) -> tuple[torch.FloatTensor, tuple[torch.FloatTensor, torch.FloatTensor] | None]:
+ residual = hidden_states
+
+ hidden_states = self.input_layernorm(hidden_states)
+
+ # Self Attention
+ attention_states, _ = self.self_attn(
+ hidden_states=hidden_states,
+ position_embeddings=position_embeddings,
+ attention_mask=attention_mask,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ **kwargs,
+ )
+ hidden_states = residual + attention_states
+
+ # Fully Connected
+ residual = hidden_states
+ hidden_states = self.post_attention_layernorm(hidden_states)
+ hidden_states = self.feed_forward(hidden_states)
+ if self.is_moe_layer:
+ hidden_states, _ = hidden_states
+ hidden_states = residual + hidden_states.view(residual.shape)
+ return hidden_states
+
+
+@auto_docstring
+class Llama4PreTrainedModel(PreTrainedModel):
+ config: Llama4Config
+ input_modalities = ("image", "text")
+ supports_gradient_checkpointing = True
+ _skip_keys_device_placement = ["past_key_values"]
+ _supports_flash_attn = False
+ _supports_sdpa = True
+ _supports_flex_attn = True
+
+ _can_compile_fullgraph = True
+ _supports_attention_backend = True
+
+ @torch.no_grad()
+ def _init_weights(self, module):
+ super()._init_weights(module)
+ std = (
+ self.config.initializer_range
+ if hasattr(self.config, "initializer_range")
+ else self.config.text_config.initializer_range
+ )
+ if isinstance(module, Llama4TextExperts):
+ init.normal_(module.gate_up_proj, mean=0.0, std=std)
+ init.normal_(module.down_proj, mean=0.0, std=std)
+ elif isinstance(module, Llama4VisionRotaryEmbedding):
+ init.copy_(module.freqs_ci, module._compute_freqs_ci(module.config))
+ elif isinstance(module, Llama4VisionModel):
+ init.normal_(module.class_embedding, std=module.scale)
+ init.normal_(module.positional_embedding_vlm, std=module.scale)
+
+
+@auto_docstring
+class Llama4TextModel(Llama4PreTrainedModel):
+ _no_split_modules = ["Llama4TextDecoderLayer"]
+ base_model_prefix = "model"
+ input_modalities = ("text",)
+ config: Llama4TextConfig
+ _can_record_outputs = {
+ "attentions": Llama4TextAttention,
+ "hidden_states": Llama4TextDecoderLayer,
+ "router_logits": Llama4TextMoe,
+ }
+
+ def __init__(self, config: Llama4TextConfig):
+ super().__init__(config)
+ self.padding_idx = config.pad_token_id
+ self.vocab_size = config.vocab_size
+
+ self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)
+ self.layers = nn.ModuleList(
+ [Llama4TextDecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
+ )
+ self.norm = Llama4TextRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
+ self.rotary_emb = Llama4TextRotaryEmbedding(config=config)
+ self.gradient_checkpointing = False
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @can_return_tuple
+ @merge_with_config_defaults
+ @capture_outputs
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ use_cache: bool | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | BaseModelOutputWithPast:
+ 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 None:
+ inputs_embeds = self.embed_tokens(input_ids.to(self.embed_tokens.weight.device))
+
+ if use_cache and past_key_values is None:
+ past_key_values = DynamicCache(config=self.config)
+
+ if position_ids is None:
+ past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
+ position_ids = torch.arange(inputs_embeds.shape[1], device=inputs_embeds.device) + past_seen_tokens
+ position_ids = position_ids.unsqueeze(0)
+
+ # It may already have been prepared by e.g. `generate`
+ if not isinstance(causal_mask_mapping := attention_mask, dict):
+ # Prepare mask arguments
+ mask_kwargs = {
+ "config": self.config,
+ "inputs_embeds": inputs_embeds,
+ "attention_mask": attention_mask,
+ "past_key_values": past_key_values,
+ "position_ids": position_ids,
+ }
+ # Create the masks
+ causal_mask_mapping = {
+ "full_attention": create_causal_mask(**mask_kwargs),
+ "chunked_attention": create_chunked_causal_mask(**mask_kwargs),
+ }
+
+ hidden_states = inputs_embeds
+
+ # create position embeddings to be shared across the decoder layers
+ freq_cis = self.rotary_emb(hidden_states, position_ids)
+
+ for i, decoder_layer in enumerate(self.layers[: self.config.num_hidden_layers]):
+ hidden_states = decoder_layer(
+ hidden_states,
+ attention_mask=causal_mask_mapping[self.config.layer_types[i]],
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ use_cache=use_cache,
+ position_embeddings=freq_cis,
+ **kwargs,
+ )
+ hidden_states = self.norm(hidden_states)
+
+ return BaseModelOutputWithPast(
+ last_hidden_state=hidden_states,
+ past_key_values=past_key_values if use_cache else None,
+ )
+
+
+class Llama4ForCausalLM(Llama4PreTrainedModel, GenerationMixin):
+ _no_split_modules = ["Llama4TextDecoderLayer"]
+ base_model_prefix = "language_model"
+ _tied_weights_keys = {"lm_head.weight": "model.embed_tokens.weight"}
+ _tp_plan = {"lm_head": "colwise_gather_output"}
+ config: Llama4TextConfig
+
+ def __init__(self, config: Llama4TextConfig):
+ super().__init__(config)
+ self.model = Llama4TextModel(config)
+ self.vocab_size = config.vocab_size
+ self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
+
+ # Initialize weights and apply final processing
+ self.post_init()
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | CausalLMOutputWithPast:
+ r"""
+ 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]`.
+
+ Example:
+
+ ```python
+ >>> from transformers import AutoTokenizer, Llama4ForCausalLM
+
+ >>> model = Llama4ForCausalLM.from_pretrained("meta-llama4/Llama4-2-7b-hf")
+ >>> tokenizer = AutoTokenizer.from_pretrained("meta-llama4/Llama4-2-7b-hf")
+
+ >>> prompt = "Hey, are you conscious? Can you talk to me?"
+ >>> inputs = tokenizer(prompt, return_tensors="pt")
+
+ >>> # Generate
+ >>> generate_ids = model.generate(inputs.input_ids, max_length=30)
+ >>> tokenizer.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]
+ "Hey, are you conscious? Can you talk to me?\nI'm not conscious, but I can talk to you."
+ ```"""
+ outputs = self.model(
+ input_ids=input_ids,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ hidden_states = outputs[0]
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(logits=logits, labels=labels, vocab_size=self.config.vocab_size, **kwargs)
+
+ return CausalLMOutputWithPast(
+ loss=loss,
+ logits=logits,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ )
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ Base class for Llava causal language model (or autoregressive) outputs.
+ """
+)
+class Llama4CausalLMOutputWithPast(ModelOutput):
+ r"""
+ loss (`torch.FloatTensor` of shape `(1,)`, *optional*, returned when `labels` is provided):
+ Language modeling loss (for next-token prediction).
+ logits (`torch.FloatTensor` of shape `(batch_size, sequence_length, config.vocab_size)`):
+ Prediction scores of the language modeling head (scores for each vocabulary token before SoftMax).
+ past_key_values (`Cache`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
+ It is a [`~cache_utils.Cache`] instance. For more details, see our [kv cache guide](https://huggingface.co/docs/transformers/en/kv_cache).
+
+ Contains pre-computed hidden-states (key and values in the self-attention blocks) that can be used (see
+ `past_key_values` input) to speed up sequential decoding.
+ image_hidden_states (`torch.FloatTensor`, *optional*):
+ A `torch.FloatTensor` of size (batch_size, num_images, sequence_length, hidden_size)`.
+ image_hidden_states of the model produced by the vision encoder and after projecting the last hidden state.
+ """
+
+ loss: torch.FloatTensor | None = None
+ logits: torch.FloatTensor | None = None
+ past_key_values: Cache | None = None
+ hidden_states: tuple[torch.FloatTensor] | None = None
+ attentions: tuple[torch.FloatTensor] | None = None
+ image_hidden_states: torch.FloatTensor | None = None
+
+
+class Llama4VisionMLP2(torch.nn.Module):
+ def __init__(self, config):
+ super().__init__()
+ self.hidden_size = config.hidden_size
+ self.intermediate_size = config.intermediate_size
+ self.fc1 = nn.Linear(self.intermediate_size, config.projector_input_dim, bias=False)
+ self.fc2 = nn.Linear(config.projector_output_dim, config.projector_output_dim, bias=False)
+ self.activation_fn = nn.GELU() # ACT2FN[config.hidden_act]
+ self.dropout = config.projector_dropout
+
+ def forward(self, hidden_states):
+ hidden_states = self.fc1(hidden_states)
+ hidden_states = self.activation_fn(hidden_states)
+ hidden_states = F.dropout(hidden_states, p=self.dropout, training=self.training)
+ return self.activation_fn(self.fc2(hidden_states))
+
+
+class Llama4MultiModalProjector(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+ self.linear_1 = nn.Linear(
+ config.vision_config.vision_output_dim,
+ config.text_config.hidden_size,
+ bias=False,
+ )
+
+ def forward(self, image_features):
+ hidden_states = self.linear_1(image_features)
+ return hidden_states
+
+
+def pixel_shuffle(input_tensor, shuffle_ratio):
+ # input_tensor: [batch_size, num_patches, channels]
+ batch_size, num_patches, channels = input_tensor.shape
+ patch_size = int(math.sqrt(num_patches))
+
+ input_tensor = input_tensor.view(batch_size, patch_size, patch_size, -1)
+ batch_size, height, width, channels = input_tensor.size()
+
+ reshaped_tensor = input_tensor.view(batch_size, height, int(width * shuffle_ratio), int(channels / shuffle_ratio))
+ reshaped_tensor = reshaped_tensor.permute(0, 2, 1, 3).contiguous()
+
+ reshaped_tensor = reshaped_tensor.view(
+ batch_size, int(height * shuffle_ratio), int(width * shuffle_ratio), int(channels / (shuffle_ratio**2))
+ )
+ reshaped_tensor = reshaped_tensor.permute(0, 2, 1, 3).contiguous()
+
+ output_tensor = reshaped_tensor.view(batch_size, -1, reshaped_tensor.shape[-1])
+ return output_tensor
+
+
+class Llama4VisionPixelShuffleMLP(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+ self.pixel_shuffle_ratio = config.pixel_shuffle_ratio
+ self.inner_dim = int(config.projector_input_dim // (self.pixel_shuffle_ratio**2))
+ self.output_dim = config.projector_output_dim
+ self.mlp = Llama4VisionMLP2(config)
+
+ def forward(self, encoded_patches: torch.Tensor) -> torch.Tensor:
+ encoded_patches = pixel_shuffle(encoded_patches, self.pixel_shuffle_ratio)
+ return self.mlp(encoded_patches)
+
+
+# TODO there is a different RoPE for vision encoder, defined as below
+def reshape_for_broadcast(freqs_ci: torch.Tensor, query: torch.Tensor):
+ ndim = query.ndim
+ shape = [d if i == 1 or i == ndim - 1 else 1 for i, d in enumerate(query.shape)]
+ return freqs_ci.view(*shape)
+
+
+def vision_apply_rotary_emb(
+ query: torch.Tensor,
+ key: torch.Tensor,
+ freqs_ci: torch.Tensor,
+) -> tuple[torch.Tensor, torch.Tensor]:
+ query_ = torch.view_as_complex(query.float().reshape(*query.shape[:-1], -1, 2))
+ key_ = torch.view_as_complex(key.float().reshape(*key.shape[:-1], -1, 2))
+ freqs_ci = reshape_for_broadcast(freqs_ci=freqs_ci, query=query_) # freqs_ci[:,:,None,:]
+ freqs_ci = freqs_ci.to(query_.device)
+ query_out = torch.view_as_real(query_ * freqs_ci).flatten(3)
+ key_out = torch.view_as_real(key_ * freqs_ci).flatten(3)
+ return query_out.type_as(query), key_out.type_as(key) # but this drops to 8e-3
+
+
+class Llama4VisionAttention(nn.Module):
+ def __init__(self, config: Llama4VisionConfig):
+ super().__init__()
+ self.config = config
+ self.embed_dim = config.hidden_size
+ self.num_heads = config.num_attention_heads
+ self.head_dim = config.hidden_size // config.num_attention_heads
+ self.num_key_value_groups = 1
+ self.attention_dropout = config.attention_dropout
+ self.scaling = self.head_dim**-0.5
+
+ self.q_proj = nn.Linear(self.embed_dim, self.num_heads * self.head_dim, bias=True)
+ self.k_proj = nn.Linear(self.embed_dim, self.num_heads * self.head_dim, bias=True)
+ self.v_proj = nn.Linear(self.embed_dim, self.num_heads * self.head_dim, bias=True)
+ self.o_proj = nn.Linear(self.num_heads * self.head_dim, self.embed_dim, bias=True)
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ freqs_ci: torch.Tensor,
+ attention_mask: torch.Tensor | None = None,
+ past_key_values: Cache | None = None,
+ **kwargs: Unpack[FlashAttentionKwargs],
+ ) -> tuple[torch.Tensor, torch.Tensor | None, tuple[torch.Tensor] | None]:
+ input_shape = hidden_states.shape[:-1]
+ hidden_shape = (*input_shape, -1, self.head_dim)
+
+ query_states = self.q_proj(hidden_states).view(hidden_shape)
+ key_states = self.k_proj(hidden_states).view(hidden_shape)
+ value_states = self.v_proj(hidden_states).view(hidden_shape)
+
+ query_states, key_states = vision_apply_rotary_emb(query_states, key_states, freqs_ci=freqs_ci)
+
+ query_states = query_states.transpose(1, 2)
+ key_states = key_states.transpose(1, 2)
+ value_states = value_states.transpose(1, 2)
+
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
+ self.config._attn_implementation, vision_eager_attention_forward
+ )
+
+ attn_output, attn_weights = attention_interface(
+ self,
+ query_states,
+ key_states,
+ value_states,
+ None,
+ dropout=0.0 if not self.training else self.attention_dropout,
+ scaling=None, # TODO Might be enforced here for TP compatibility as scaling is not just sqrt(head_dim)
+ is_causal=False, # HAS TO BE ENFORCED
+ **kwargs,
+ )
+
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
+ attn_output = self.o_proj(attn_output)
+ return attn_output, attn_weights
+
+
+class Llama4VisionMLP(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+ self.config = config
+ self.activation_fn = nn.GELU() # ACT2FN[config.hidden_act]
+ self.fc1 = nn.Linear(config.hidden_size, config.intermediate_size, bias=True)
+ self.fc2 = nn.Linear(config.intermediate_size, config.hidden_size, bias=True)
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ hidden_states = self.fc1(hidden_states)
+ hidden_states = self.activation_fn(hidden_states)
+ hidden_states = self.fc2(hidden_states)
+ return hidden_states
+
+
+class Llama4VisionEncoderLayer(GradientCheckpointingLayer):
+ def __init__(self, config: Llama4VisionConfig):
+ super().__init__()
+ self.hidden_size = config.hidden_size
+
+ self.self_attn = Llama4VisionAttention(config)
+ self.mlp = Llama4VisionMLP(config)
+
+ self.input_layernorm = nn.LayerNorm(config.hidden_size)
+ self.post_attention_layernorm = nn.LayerNorm(config.hidden_size)
+
+ def forward(
+ self,
+ hidden_state: torch.Tensor,
+ freqs_ci: torch.Tensor,
+ attention_mask: torch.Tensor | None = None,
+ output_attentions: bool | None = None,
+ ):
+ # Self Attention
+ residual = hidden_state
+
+ hidden_state = self.input_layernorm(hidden_state)
+
+ hidden_state, attn_weights = self.self_attn(
+ hidden_state,
+ freqs_ci=freqs_ci,
+ attention_mask=attention_mask,
+ )
+ hidden_state = residual + hidden_state
+
+ # Feed forward
+ residual = hidden_state
+ hidden_state = self.post_attention_layernorm(hidden_state)
+ hidden_state = self.mlp(hidden_state)
+ hidden_state = residual + hidden_state
+
+ outputs = (hidden_state,)
+
+ if output_attentions:
+ outputs += (attn_weights,)
+
+ return outputs
+
+
+class Llama4VisionEncoder(nn.Module):
+ """
+ Transformer encoder consisting of `config.num_hidden_layers` self attention layers. Each layer is a
+ [`Llama4VisionEncoderLayer`].
+
+ Args:
+ config: Llama4VisionConfig
+ """
+
+ def __init__(self, config: Llama4VisionConfig):
+ super().__init__()
+ self.config = config
+ self.layers = nn.ModuleList([Llama4VisionEncoderLayer(config) for _ in range(config.num_hidden_layers)])
+ self.gradient_checkpointing = False
+ self.config = config
+
+ def forward(
+ self,
+ hidden_states: torch.Tensor,
+ freqs_ci: torch.Tensor, # TODO move this to an attribute instead of keeping it around
+ attention_mask: torch.Tensor | None = None,
+ output_attentions: bool | None = None,
+ output_hidden_states: bool | None = None,
+ return_dict: bool | None = None,
+ ) -> tuple | BaseModelOutput:
+ r"""
+ Args:
+ inputs_embeds (`torch.FloatTensor` of shape `(batch_size, sequence_length, hidden_size)`):
+ Optionally, instead of passing `input_ids` you can choose to directly pass an embedded representation.
+ This is useful if you want more control over how to convert `input_ids` indices into associated vectors
+ than the model's internal embedding lookup matrix.
+ attention_mask (`torch.Tensor` of shape `(batch_size, sequence_length)`, *optional*):
+ Mask to avoid performing attention on padding token indices. Mask values selected in `[0, 1]`:
+
+ - 1 for tokens that are **not masked**,
+ - 0 for tokens that are **masked**.
+
+ [What are attention masks?](../glossary#attention-mask)
+ output_attentions (`bool`, *optional*):
+ Whether or not to return the attentions tensors of all attention layers. See `attentions` under
+ returned tensors for more detail.
+ output_hidden_states (`bool`, *optional*):
+ Whether or not to return the hidden states of all layers. See `hidden_states` under returned tensors
+ for more detail.
+ return_dict (`bool`, *optional*):
+ Whether or not to return a [`~utils.ModelOutput`] instead of a plain tuple.
+ """
+ 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.return_dict
+
+ encoder_states = () if output_hidden_states else None
+ all_attentions = () if output_attentions else None
+
+ for encoder_layer in self.layers:
+ if output_hidden_states:
+ encoder_states = encoder_states + (hidden_states,)
+
+ layer_outputs = encoder_layer(
+ hidden_state=hidden_states,
+ attention_mask=attention_mask,
+ output_attentions=output_attentions,
+ freqs_ci=freqs_ci,
+ )
+
+ if output_attentions:
+ all_attentions = all_attentions + (layer_outputs[1],)
+
+ hidden_states = layer_outputs[0]
+
+ if output_hidden_states:
+ encoder_states = encoder_states + (hidden_states,)
+
+ if not return_dict:
+ return tuple(v for v in [hidden_states, encoder_states, all_attentions] if v is not None)
+ return BaseModelOutput(
+ last_hidden_state=hidden_states, hidden_states=encoder_states, attentions=all_attentions
+ )
+
+
+class Llama4UnfoldConvolution(nn.Module):
+ def __init__(self, config):
+ super().__init__()
+ kernel_size = config.patch_size
+ if isinstance(kernel_size, int):
+ kernel_size = (kernel_size, kernel_size)
+ self.unfold = torch.nn.Unfold(kernel_size=kernel_size, stride=config.patch_size)
+ self.linear = nn.Linear(
+ config.num_channels * kernel_size[0] * kernel_size[1],
+ config.hidden_size,
+ bias=False,
+ )
+
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
+ hidden_states = self.unfold(hidden_states)
+ hidden_states = hidden_states.permute(0, 2, 1)
+ hidden_states = self.linear(hidden_states)
+ return hidden_states
+
+
+class Llama4VisionRotaryEmbedding(nn.Module):
+ def __init__(self, config: Llama4VisionConfig):
+ super().__init__()
+ self.config = config
+ self.register_buffer("freqs_ci", self._compute_freqs_ci(config), persistent=False)
+
+ @staticmethod
+ def _compute_freqs_ci(config):
+ idx = config.image_size // config.patch_size
+ img_idx = torch.arange(idx**2, dtype=torch.int32).reshape(idx**2, 1)
+ img_idx = torch.cat([img_idx, img_idx[:1]], dim=0)
+ img_idx[-1, -1] = -2 # ID_CLS_TOKEN
+ frequencies_x = img_idx % idx # get the coordinates of the 2d matrix along x
+ frequencies_y = img_idx // idx # get the coordinates of the 2d matrix along y
+ freq_dim = config.hidden_size // config.num_attention_heads // 2
+ rope_freq = 1.0 / (
+ config.rope_parameters["rope_theta"]
+ ** (torch.arange(0, freq_dim, 2)[: (freq_dim // 2)].float() / freq_dim)
+ )
+ freqs_x = ((frequencies_x + 1)[..., None] * rope_freq[None, None, :]).repeat_interleave(2, dim=-1)
+ freqs_y = ((frequencies_y + 1)[..., None] * rope_freq[None, None, :]).repeat_interleave(2, dim=-1)
+ freqs = torch.cat([freqs_x, freqs_y], dim=-1).float().contiguous()[..., ::2]
+ freqs = freqs.masked_fill(img_idx.reshape(-1, 1, 1) < 0, 0)
+ freq_cis = torch.view_as_complex(torch.stack([torch.cos(freqs), torch.sin(freqs)], dim=-1))
+ return freq_cis # idx**2, idx**2, idx * 2
+
+ def forward(self, hidden_states):
+ return self.freqs_ci.to(hidden_states.device)
+
+
+class Llama4VisionModel(Llama4PreTrainedModel):
+ base_model_prefix = "vision_model"
+ input_modalities = ("image",)
+ _no_split_modules = ["Llama4VisionEncoderLayer"]
+ config: Llama4VisionConfig
+
+ def __init__(self, config: Llama4VisionConfig):
+ super().__init__(config)
+ self.image_size = config.image_size
+ self.patch_size = config.patch_size
+ self.hidden_size = config.hidden_size
+ self.num_channels = config.num_channels
+
+ self.num_patches = (self.image_size // self.patch_size) ** 2 + 1
+ self.scale = config.hidden_size**-0.5
+
+ self.patch_embedding = Llama4UnfoldConvolution(config)
+
+ self.class_embedding = nn.Parameter(self.scale * torch.randn(self.hidden_size))
+ self.positional_embedding_vlm = nn.Parameter(self.scale * torch.randn(self.num_patches, self.hidden_size))
+ self.rotary_embedding = Llama4VisionRotaryEmbedding(config)
+
+ # layer norms
+ self.layernorm_pre = nn.LayerNorm(self.hidden_size)
+ self.layernorm_post = nn.LayerNorm(self.hidden_size)
+
+ # encoders
+ self.model = Llama4VisionEncoder(config)
+ self.vision_adapter = Llama4VisionPixelShuffleMLP(config)
+ self.post_init()
+
+ def get_input_embeddings(self):
+ """
+ This function is used to fetch the first embedding layer to activate grads on inputs.
+ """
+ return self.patch_embedding
+
+ def forward(
+ self,
+ pixel_values: torch.Tensor,
+ attention_mask: torch.Tensor | None = None,
+ output_attentions: bool | None = None,
+ output_hidden_states: bool | None = None,
+ return_dict: bool | None = None,
+ **kwargs,
+ ) -> BaseModelOutputWithPooling | tuple[torch.Tensor, ...]:
+ r"""
+
+ Example:
+
+ ```python
+ >>> from PIL import Image
+ >>> import httpx
+ >>> from io import BytesIO
+ >>> from transformers import AutoProcessor, MllamaVisionModel
+
+ >>> checkpoint = "meta-llama/Llama-3.2-11B-Vision"
+ >>> model = MllamaVisionModel.from_pretrained(checkpoint)
+ >>> processor = AutoProcessor.from_pretrained(checkpoint)
+
+ >>> url = "https://www.ilankelman.org/stopsigns/australia.jpg"
+ >>> with httpx.stream("GET", url) as response:
+ ... image = Image.open(BytesIO(response.read()))
+ >>> inputs = processor(images=image, return_tensors="pt")
+
+ >>> output = model(**inputs)
+
+ >>> print(output.last_hidden_state.shape)
+ torch.Size([1, 1, 4, 1025, 7680])
+ ```
+ """
+ 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.return_dict
+
+ # num_concurrent_media and num_chunks are both currently 1
+ batch_size_times_num_tiles, num_channels, height, width = pixel_values.shape
+ num_concurrent_media = 1
+ num_chunks = 1
+ hidden_state = self.patch_embedding(pixel_values)
+ _, num_patches, hidden_dim = hidden_state.shape
+
+ # Add cls token
+ hidden_state = hidden_state.reshape(
+ batch_size_times_num_tiles * num_concurrent_media * num_chunks, num_patches, hidden_dim
+ )
+ class_embedding = self.class_embedding.expand(hidden_state.shape[0], 1, hidden_state.shape[-1])
+ hidden_state = torch.cat([hidden_state, class_embedding], dim=1)
+ num_patches += 1
+
+ # Position embeddings
+ hidden_state = hidden_state.reshape(
+ batch_size_times_num_tiles * num_concurrent_media, num_chunks, num_patches, hidden_dim
+ )
+ positional_embedding = self.positional_embedding_vlm.to(dtype=hidden_state.dtype, device=hidden_state.device)
+ hidden_state = hidden_state + positional_embedding
+
+ hidden_state = self.layernorm_pre(hidden_state)
+
+ hidden_state = hidden_state.view(batch_size_times_num_tiles, -1, hidden_dim)
+ freqs_ci = self.rotary_embedding(pixel_values)
+
+ output = self.model(
+ hidden_state,
+ attention_mask=None,
+ output_hidden_states=output_hidden_states,
+ output_attentions=output_attentions,
+ freqs_ci=freqs_ci,
+ )
+
+ hidden_state = output.last_hidden_state
+
+ hidden_state = self.layernorm_post(hidden_state)
+
+ hidden_state = hidden_state[:, :-1, :]
+
+ # now, we use Llama4VisionPixelShuffle + mlp to project embeddings
+ hidden_state = self.vision_adapter(hidden_state)
+
+ hidden_states = output.hidden_states if output_hidden_states else None
+
+ if output_attentions:
+ attentions = output[2]
+ else:
+ attentions = None
+
+ if not return_dict:
+ return tuple(v for v in [hidden_state, hidden_states, attentions] if v is not None)
+
+ return BaseModelOutputWithPooling(
+ last_hidden_state=hidden_state,
+ hidden_states=hidden_states,
+ attentions=attentions,
+ )
+
+
+class Llama4ForConditionalGeneration(Llama4PreTrainedModel, GenerationMixin):
+ _no_split_modules = ["Llama4TextDecoderLayer", "Llama4VisionEncoderLayer"]
+ _tp_plan = {}
+ base_model_prefix = "model"
+ config: Llama4Config
+
+ def __init__(self, config: Llama4Config):
+ super().__init__(config)
+ self.vision_model = Llama4VisionModel(config.vision_config)
+
+ self.multi_modal_projector = Llama4MultiModalProjector(config)
+ self.language_model = Llama4ForCausalLM(config.text_config)
+ self.vocab_size = config.text_config.vocab_size
+ if hasattr(self.config, "pad_token_id"):
+ self.pad_token_id = self.config.pad_token_id
+ else:
+ self.pad_token_id = self.config.text_config.pad_token_id or -1
+
+ self.post_init()
+
+ def get_input_embeddings(self):
+ return self.language_model.get_input_embeddings()
+
+ def set_input_embeddings(self, value):
+ self.language_model.set_input_embeddings(value)
+
+ def get_output_embeddings(self):
+ return self.language_model.get_output_embeddings()
+
+ def set_output_embeddings(self, new_embeddings):
+ self.language_model.set_output_embeddings(new_embeddings)
+
+ def set_decoder(self, decoder):
+ self.language_model.set_decoder(decoder)
+
+ def get_decoder(self):
+ return self.language_model.get_decoder()
+
+ @merge_with_config_defaults
+ @capture_outputs(tie_last_hidden_states=False)
+ @auto_docstring(custom_intro="Obtains image last hidden states from the vision tower and apply al projection.")
+ def get_image_features(
+ self,
+ pixel_values: torch.FloatTensor,
+ vision_feature_select_strategy: str,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | BaseModelOutputWithPooling:
+ r"""
+ pixel_values (`torch.FloatTensor]` of shape `(batch_size, channels, height, width)`)
+ The tensors corresponding to the input images.
+ vision_feature_select_strategy (`str`):
+ The feature selection strategy used to select the vision feature from the vision backbone.
+ Can be one of `"default"` or `"full"`
+ """
+ kwargs = {k: v for k, v in kwargs.items() if v is not None}
+ return self.vision_model(pixel_values, **kwargs)
+
+ def get_placeholder_mask(
+ self, input_ids: torch.LongTensor, inputs_embeds: torch.FloatTensor, image_features: torch.FloatTensor
+ ):
+ """
+ Obtains multimodal placeholder mask from `input_ids` or `inputs_embeds`, and checks that the placeholder token count is
+ equal to the length of multimodal features. If the lengths are different, an error is raised.
+ """
+ if input_ids is None:
+ special_image_mask = inputs_embeds == self.get_input_embeddings()(
+ torch.tensor(self.config.image_token_id, dtype=torch.long, device=inputs_embeds.device)
+ )
+ special_image_mask = special_image_mask.all(-1)
+ else:
+ special_image_mask = input_ids == self.config.image_token_id
+
+ n_image_tokens = special_image_mask.sum()
+ special_image_mask = special_image_mask.unsqueeze(-1).expand_as(inputs_embeds).to(inputs_embeds.device)
+ torch_compilable_check(
+ inputs_embeds[special_image_mask].numel() == image_features.numel(),
+ f"Image features and image tokens do not match, tokens: {n_image_tokens}, features: {image_features.shape[0]}",
+ )
+ return special_image_mask
+
+ @merge_with_config_defaults
+ @capture_outputs(tie_last_hidden_states=False)
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ pixel_values: torch.FloatTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ vision_feature_select_strategy: str | None = None,
+ labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ output_attentions: bool | None = None,
+ output_hidden_states: bool | None = None,
+ return_dict: bool | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | Llama4CausalLMOutputWithPast:
+ r"""
+ 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]`.
+
+ Example:
+
+ ```python
+ >>> from PIL import Image
+ >>> import httpx
+ >>> from io import BytesIO
+ >>> from transformers import AutoProcessor, LlavaForConditionalGeneration
+
+ >>> model = LlavaForConditionalGeneration.from_pretrained("llava-hf/llava-1.5-7b-hf")
+ >>> processor = AutoProcessor.from_pretrained("llava-hf/llava-1.5-7b-hf")
+
+ >>> prompt = "USER: \nWhat's the content of the image? ASSISTANT:"
+ >>> url = "https://www.ilankelman.org/stopsigns/australia.jpg"
+ >>> with httpx.stream("GET", url) as response:
+ ... image = Image.open(BytesIO(response.read()))
+
+ >>> inputs = processor(images=image, text=prompt, return_tensors="pt")
+
+ >>> # Generate
+ >>> generate_ids = model.generate(**inputs, max_new_tokens=15)
+ >>> processor.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]
+ "USER: \nWhat's the content of the image? ASSISTANT: The image features a busy city street with a stop sign prominently displayed"
+ ```"""
+
+ 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.return_dict
+
+ if (input_ids is None) ^ (inputs_embeds is not None):
+ raise ValueError("You must specify exactly one of input_ids or inputs_embeds")
+
+ if pixel_values is not None and inputs_embeds is not None:
+ raise ValueError(
+ "You cannot specify both pixel_values and inputs_embeds at the same time, and must specify either one"
+ )
+
+ if inputs_embeds is None:
+ inputs_embeds = self.get_input_embeddings()(input_ids)
+
+ if pixel_values is not None:
+ image_features = self.get_image_features(
+ pixel_values=pixel_values,
+ vision_feature_select_strategy=vision_feature_select_strategy,
+ return_dict=True,
+ ).last_hidden_state
+
+ vision_flat = image_features.view(-1, image_features.size(-1))
+ projected_vision_flat = self.multi_modal_projector(vision_flat).to(
+ inputs_embeds.device, inputs_embeds.dtype
+ )
+ special_image_mask = self.get_placeholder_mask(
+ input_ids, inputs_embeds=inputs_embeds, image_features=projected_vision_flat
+ )
+ inputs_embeds = inputs_embeds.masked_scatter(special_image_mask, projected_vision_flat)
+
+ outputs = self.language_model(
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ output_attentions=output_attentions,
+ output_hidden_states=output_hidden_states,
+ return_dict=return_dict,
+ logits_to_keep=logits_to_keep,
+ **kwargs,
+ )
+
+ logits = outputs[0]
+
+ loss = None
+ if labels is not None:
+ # Shift so that tokens < n predict n
+ if attention_mask is not None:
+ # we use the input attention mask to shift the logits and labels, because it is 2D.
+ # we also crop attn mask in case it is longer, which happens in PrefixTuning with peft
+ shift_attention_mask = attention_mask[:, -(logits.shape[1] - 1) :].to(logits.device)
+ shift_logits = logits[..., :-1, :][shift_attention_mask.to(logits.device) != 0].contiguous()
+ shift_labels = labels[..., 1:][shift_attention_mask.to(labels.device) != 0].contiguous()
+ else:
+ shift_logits = logits[..., :-1, :].contiguous()
+ shift_labels = labels[..., 1:].contiguous()
+ # Flatten the tokens
+ loss_fct = nn.CrossEntropyLoss()
+ loss = loss_fct(
+ shift_logits.view(-1, shift_logits.size(-1)), shift_labels.view(-1).to(shift_logits.device)
+ )
+
+ if not return_dict:
+ output = (logits,) + outputs[1:]
+ return (loss,) + output if loss is not None else output
+
+ return Llama4CausalLMOutputWithPast(
+ loss=loss,
+ logits=logits,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ image_hidden_states=image_features if pixel_values is not None else None,
+ )
+
+ def prepare_inputs_for_generation(
+ self,
+ input_ids,
+ past_key_values=None,
+ inputs_embeds=None,
+ pixel_values=None,
+ attention_mask=None,
+ logits_to_keep=None,
+ is_first_iteration=False,
+ **kwargs,
+ ):
+ # Overwritten -- in specific circumstances we don't want to forward image inputs to the model
+
+ model_inputs = self.language_model.prepare_inputs_for_generation(
+ input_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ attention_mask=attention_mask,
+ logits_to_keep=logits_to_keep,
+ is_first_iteration=is_first_iteration,
+ **kwargs,
+ )
+
+ if is_first_iteration or not kwargs.get("use_cache", True):
+ # Pixel values are used only in the first iteration if available
+ # In subsequent iterations, they are already merged with text and cached
+ # NOTE: first iteration doesn't have to be prefill, it can be the first
+ # iteration with a question and cached system prompt (continue generate from cache)
+ model_inputs["pixel_values"] = pixel_values
+
+ return model_inputs
+
+
+__all__ = [
+ "Llama4PreTrainedModel",
+ "Llama4TextModel",
+ "Llama4VisionModel",
+ "Llama4ForCausalLM",
+ "Llama4ForConditionalGeneration",
+]
diff --git a/third_party/transformers/src/transformers/models/llama4/processing_llama4.py b/third_party/transformers/src/transformers/models/llama4/processing_llama4.py
new file mode 100644
index 0000000000000000000000000000000000000000..f67e37a1e80a228410510df10cd22455c23b56a9
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/llama4/processing_llama4.py
@@ -0,0 +1,201 @@
+# Copyright 2025 HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+from transformers.processing_utils import ProcessingKwargs, ProcessorMixin, Unpack
+from transformers.tokenization_utils_base import PreTokenizedInput, TextInput
+
+from ...image_processing_utils import BatchFeature
+from ...image_utils import ImageInput, make_flat_list_of_images
+from ...utils import auto_docstring
+
+
+class Llama4ProcessorKwargs(ProcessingKwargs, total=False):
+ _defaults = {
+ "text_kwargs": {
+ "padding_side": "left",
+ },
+ }
+
+
+chat_template = "{{- bos_token }}\n{%- if custom_tools is defined %}\n {%- set tools = custom_tools %}\n{%- endif %}\n{%- if not tools_in_user_message is defined %}\n {%- set tools_in_user_message = true %}\n{%- endif %}\n{%- if not date_string is defined %}\n {%- if strftime_now is defined %}\n {%- set date_string = strftime_now(\"%d %b %Y\") %}\n {%- else %}\n {%- set date_string = \"26 Jul 2024\" %}\n {%- endif %}\n{%- endif %}\n{%- if not tools is defined %}\n {%- set tools = none %}\n{%- endif %}\n\n{#- This block extracts the system message, so we can slot it into the right place. #}\n{%- if messages[0]['role'] == 'system' %} \n {%- if messages[0]['content'] is string %}\n {%- set system_message = messages[0]['content']|trim %}\n {%- else %}\n {#- FIXME: The processor requires an array, always. #}\n {%- set system_message = messages[0]['content'][0]['text']|trim %}\n {%- endif %}\n {%- set messages = messages[1:] %}\n {%- set user_supplied_system_message = true %}\n{%- else %}\n {%- set system_message = \"\" %}\n {%- set user_supplied_system_message = false %}\n{%- endif %}\n\n{#- System message if the user supplied one #}\n{%- if user_supplied_system_message %}\n {{- \"<|header_start|>system<|header_end|>\n\n\" }}\n {%- if tools is not none %}\n {{- \"Environment: ipython\n\" }}\n {%- endif %}\n {%- if tools is not none and not tools_in_user_message %}\n {{- \"You have access to the following functions. To call a function, please respond with JSON for a function call.\" }}\n {{- 'Respond in the format {\"name\": function name, \"parameters\": dictionary of argument name and its value}.' }}\n {{- \"Do not use variables.\n\n\" }}\n {%- for t in tools %}\n {{- t | tojson(indent=4) }}\n {{- \"\n\n\" }}\n {%- endfor %}\n {%- endif %}\n {{- system_message }}\n {{- \"<|eot|>\" }}\n{%- endif %}\n\n{#- Custom tools are passed in a user message with some extra guidance #}\n{%- if tools_in_user_message and not tools is none %}\n {#- Extract the first user message so we can plug it in here #}\n {%- if messages | length != 0 %}\n {%- set first_user_message = messages[0]['content']|trim %}\n {%- set messages = messages[1:] %}\n {%- else %}\n {{- raise_exception(\"Cannot put tools in the first user message when there's no first user message!\") }}\n{%- endif %}\n {{- '<|header_start|>user<|header_end|>\n\n' -}}\n {{- \"Given the following functions, please respond with a JSON for a function call \" }}\n {{- \"with its proper arguments that best answers the given prompt.\n\n\" }}\n {{- 'Respond in the format {\"name\": function name, \"parameters\": dictionary of argument name and its value}.' }}\n {{- \"Do not use variables.\n\n\" }}\n {%- for t in tools %}\n {{- t | tojson(indent=4) }}\n {{- \"\n\n\" }}\n {%- endfor %}\n {{- first_user_message + \"<|eot|>\"}}\n{%- endif %}\n\n{%- for message in messages %}\n {%- if not (message.role == 'ipython' or message.role == 'tool' or 'tool_calls' in message) %}\n {{- '<|header_start|>' + message['role'] + '<|header_end|>\n\n' }}\n {%- if message['content'] is string %}\n {{- message['content'] }}\n {%- else %}\n {%- for content in message['content'] %}\n {%- if content['type'] == 'image' %}\n {{- '<|image|>' }}\n {%- elif content['type'] == 'text' %}\n {{- content['text'] }}\n {%- endif %}\n {%- endfor %}\n {%- endif %}\n {{- \"<|eot|>\" }}\n {%- elif 'tool_calls' in message and message.tool_calls|length > 0 %}\n {{- '<|header_start|>assistant<|header_end|>\n\n' -}}\n {{- '<|python_start|>' }}\n {%- if message['content'] is string %}\n {{- message['content'] }}\n {%- else %}\n {%- for content in message['content'] %}\n {%- if content['type'] == 'image' %}\n {{- '<|image|>' }}\n {%- elif content['type'] == 'text' %}\n {{- content['text'] }}\n {%- endif %}\n {%- endfor %}\n {%- endif %}\n {{- '<|python_end|>' }}\n {%- for tool_call in message.tool_calls %}\n {{- '{\"name\": \"' + tool_call.function.name + '\", ' }}\n {{- '\"parameters\": ' }}\n {{- tool_call.function.arguments | tojson }}\n {{- \"}\" }}\n {%- endfor %}\n {{- \"<|eot|>\" }}\n {%- elif message.role == \"tool\" or message.role == \"ipython\" %}\n {{- \"<|header_start|>ipython<|header_end|>\n\n\" }}\n {%- if message.content is mapping or message.content is iterable %}\n {{- message.content | tojson }}\n {%- else %}\n {{- message.content }}\n {%- endif %}\n {{- \"<|eot|>\" }}\n {%- endif %}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|header_start|>assistant<|header_end|>\n\n' }}\n{%- endif %}\n"
+
+
+@auto_docstring
+class Llama4Processor(ProcessorMixin):
+ def __init__(
+ self,
+ image_processor=None,
+ tokenizer=None,
+ patch_size: int = 14,
+ pixel_shuffle_ratio: float = 0.5,
+ fake_image_token="<|image|>",
+ image_token="<|image|>",
+ start_of_image_token="<|image_start|>",
+ end_of_image_token="<|image_end|>",
+ patch_token="<|patch|>",
+ tile_x_separator_token="<|tile_x_separator|>",
+ tile_y_separator_token="<|tile_y_separator|>",
+ chat_template=chat_template,
+ **kwargs,
+ ):
+ r"""
+ patch_size (`int`, *optional*, defaults to 28):
+ The size of image patches for tokenization.
+ pixel_shuffle_ratio (`float`, *optional*, defaults to `0.5`):
+ The ratio used for pixel shuffling when processing images. This controls the downsampling factor
+ applied to image patches. The actual downsampling ratio is calculated as `1 / (pixel_shuffle_ratio^2)`.
+ fake_image_token (`str`, *optional*, defaults to `"<|image|>"`):
+ The placeholder token in the text that will be replaced with actual image tokens. This token serves
+ as a marker indicating where images should be inserted in the text sequence.
+ image_token (`str`, *optional*, defaults to `"<|image|>"`):
+ The token to be used to represent an image in the text.
+ start_of_image_token (`str`, *optional*, defaults to `"<|image_start|>"`):
+ The special token that marks the beginning of an image sequence in the text. This token is prepended
+ to image token sequences to delimit image boundaries.
+ end_of_image_token (`str`, *optional*, defaults to `"<|image_end|>"`):
+ The special token that marks the end of an image sequence in the text. This token is appended to
+ image token sequences to delimit image boundaries.
+ patch_token (`str`, *optional*, defaults to `"<|patch|>"`):
+ The token used to represent individual image patches. Multiple patch tokens are used to represent
+ the full image, with the number depending on the image size and patch configuration.
+ tile_x_separator_token (`str`, *optional*, defaults to `"<|tile_x_separator|>"`):
+ The token used to separate tiles (patches) horizontally within an image. This token is inserted
+ between patches in the same row when images are split into multiple tiles.
+ tile_y_separator_token (`str`, *optional*, defaults to `"<|tile_y_separator|>"`):
+ The token used to separate tiles (patches) vertically within an image. This token is inserted
+ between rows of patches when images are split into multiple tiles.
+ """
+ super().__init__(image_processor, tokenizer, chat_template=chat_template)
+
+ self.downsample_ratio = int(round(1.0 / (pixel_shuffle_ratio**2)))
+ self.patch_size = patch_size
+
+ self.fake_image_token = fake_image_token
+ self.image_token = image_token
+ self.image_token_id = tokenizer.convert_tokens_to_ids(self.image_token)
+ self.start_of_img_token = start_of_image_token
+ self.end_of_img_token = end_of_image_token
+ self.img_patch_token = patch_token
+ self.tile_token = tile_x_separator_token
+ self.tile_global_token = tile_y_separator_token
+
+ def _prompt_split_image(self, aspect_ratio, num_patches_per_chunk):
+ """
+ Create a structured string representation of image tokens
+
+ Args:
+ num_patches: Number of patches in the image
+
+ Returns:
+ String with appropriate image tokens
+ """
+ img_string = "<|image_start|>"
+ ratio_h, ratio_w = aspect_ratio
+ if ratio_h * ratio_w > 1:
+ for yy in range(ratio_h):
+ for xx in range(ratio_w):
+ img_string += "<|patch|>" * num_patches_per_chunk
+ if xx < ratio_w - 1:
+ img_string += "<|tile_x_separator|>"
+
+ img_string += "<|tile_y_separator|>"
+
+ img_string += "<|image|>"
+ img_string += "<|patch|>" * num_patches_per_chunk
+ img_string += "<|image_end|>"
+
+ return img_string
+
+ @auto_docstring
+ def __call__(
+ self,
+ images: ImageInput | None = None,
+ text: TextInput | PreTokenizedInput | list[TextInput] | list[PreTokenizedInput] | None = None,
+ **kwargs: Unpack[Llama4ProcessorKwargs],
+ ) -> BatchFeature:
+ r"""
+ Returns:
+ [`BatchFeature`]: A [`BatchFeature`] with the following fields:
+
+ - **input_ids** -- List of token ids to be fed to a model. Returned when `text` is not `None`.
+ - **attention_mask** -- List of indices specifying which tokens should be attended to by the model (when
+ `return_attention_mask=True` or if *"attention_mask"* is in `self.model_input_names` and if `text` is not
+ `None`).
+ - **pixel_values** -- Pixel values to be fed to a model. Returned when `images` is not `None`.
+ """
+ if text is None:
+ raise ValueError("You have to specify text.")
+
+ output_kwargs = self._merge_kwargs(
+ Llama4ProcessorKwargs,
+ tokenizer_init_kwargs=self.tokenizer.init_kwargs,
+ **kwargs,
+ )
+
+ if not isinstance(text, (list, tuple)):
+ text = [text]
+
+ # Process images
+ image_inputs = {}
+ if images is not None:
+ images = self.image_processor.fetch_images(images)
+ images = make_flat_list_of_images(images)
+ image_inputs = self.image_processor(images=images, **output_kwargs["images_kwargs"])
+ image_height, image_width = image_inputs["pixel_values"][0].shape[-2:]
+ num_patches_per_chunk = int(
+ (image_height // self.patch_size) * (image_width // self.patch_size) // self.downsample_ratio
+ )
+ aspect_ratios = image_inputs.pop("aspect_ratios")
+
+ total_placeholders = sum(prompt.count(self.fake_image_token) for prompt in text)
+ if total_placeholders != len(images):
+ raise ValueError(
+ f"Found {total_placeholders} placeholders across the batch, "
+ f"but have {len(images)} flattened images."
+ )
+
+ image_index = 0
+ processed_text = []
+ for prompt in text:
+ placeholder_count = prompt.count(self.fake_image_token)
+ if placeholder_count == 0:
+ # do nothing if there is no image
+ processed_text.append(prompt)
+ continue
+ prompt_splits = prompt.split(self.fake_image_token)
+ new_prompt = []
+ for local_image_index, split_part in enumerate(prompt_splits):
+ new_prompt.append(split_part)
+ if local_image_index < placeholder_count:
+ tokens_for_this_image = self._prompt_split_image(
+ aspect_ratios[image_index], num_patches_per_chunk
+ )
+ image_index += 1
+ new_prompt.append(tokens_for_this_image)
+ processed_text.append("".join(new_prompt))
+
+ if image_index != len(images):
+ raise ValueError("Number of image placeholders in the prompt does not match the number of images.")
+
+ text = processed_text
+
+ return_tensors = output_kwargs["text_kwargs"].pop("return_tensors", None)
+ text_inputs = self.tokenizer(text, **output_kwargs["text_kwargs"])
+ self._check_special_mm_tokens(text, text_inputs, modalities=["image"])
+
+ return BatchFeature(data={**text_inputs, **image_inputs}, tensor_type=return_tensors)
+
+
+__all__ = ["Llama4Processor"]
diff --git a/third_party/transformers/src/transformers/models/llava/__init__.py b/third_party/transformers/src/transformers/models/llava/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..9499aec53bdaeb597a0dfaba668d39f9ec742823
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/llava/__init__.py
@@ -0,0 +1,30 @@
+# Copyright 2024 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_llava import *
+ from .image_processing_llava import *
+ from .image_processing_pil_llava import *
+ from .modeling_llava import *
+ from .processing_llava import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/llava/configuration_llava.py b/third_party/transformers/src/transformers/models/llava/configuration_llava.py
new file mode 100644
index 0000000000000000000000000000000000000000..7548b4aec1664c79e7e483e63a5d23b221835ffe
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/llava/configuration_llava.py
@@ -0,0 +1,96 @@
+# Copyright 2023 Microsoft Research & University of Wisconsin-Madison and the HuggingFace Inc. team. All rights reserved.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Llava model configuration"""
+
+from typing import Literal
+
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...utils import auto_docstring
+from ..auto import CONFIG_MAPPING, AutoConfig
+
+
+@auto_docstring(checkpoint="llava-hf/llava-1.5-7b-hf")
+@strict
+class LlavaConfig(PreTrainedConfig):
+ r"""
+ Example:
+
+ ```python
+ >>> from transformers import LlavaForConditionalGeneration, LlavaConfig, CLIPVisionConfig, LlamaConfig
+
+ >>> # Initializing a CLIP-vision config
+ >>> vision_config = CLIPVisionConfig()
+
+ >>> # Initializing a Llama config
+ >>> text_config = LlamaConfig()
+
+ >>> # Initializing a Llava llava-1.5-7b style configuration
+ >>> configuration = LlavaConfig(vision_config, text_config)
+
+ >>> # Initializing a model from the llava-1.5-7b style configuration
+ >>> model = LlavaForConditionalGeneration(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "llava"
+ attribute_map = {
+ "image_token_id": "image_token_index",
+ }
+ sub_configs = {"text_config": AutoConfig, "vision_config": AutoConfig}
+
+ vision_config: dict | PreTrainedConfig | None = None
+ text_config: dict | PreTrainedConfig | None = None
+ image_token_index: int = 32000
+ image_seq_length: int = 576
+ projector_hidden_act: str = "gelu"
+ vision_feature_select_strategy: Literal["default", "full"] = "default"
+ vision_feature_layer: int | list[int] = -2
+ multimodal_projector_bias: bool = True
+ tie_word_embeddings: bool = False
+
+ def __post_init__(self, **kwargs):
+ if isinstance(self.vision_config, dict):
+ self.vision_config["model_type"] = self.vision_config.get("model_type", "clip_vision_model")
+ self.vision_config = CONFIG_MAPPING[self.vision_config["model_type"]](**self.vision_config)
+ elif self.vision_config is None:
+ self.vision_config = CONFIG_MAPPING["clip_vision_model"](
+ intermediate_size=4096,
+ hidden_size=1024,
+ patch_size=14,
+ image_size=336,
+ num_hidden_layers=24,
+ num_attention_heads=16,
+ vocab_size=32000,
+ projection_dim=768,
+ )
+
+ if isinstance(self.text_config, dict):
+ self.text_config["model_type"] = self.text_config.get("model_type", "llama")
+ self.text_config = CONFIG_MAPPING[self.text_config["model_type"]](**self.text_config)
+ elif self.text_config is None:
+ self.text_config = CONFIG_MAPPING["llama"]()
+
+ # The default value is `False` but this config is used with many model types
+ # Attr `tie_word_embeddings` was saved in text config for those models, so we
+ # need an ugly workaround and forward-pass the attr from text config
+ if not self.tie_word_embeddings and self.text_config.tie_word_embeddings:
+ self.tie_word_embeddings = self.text_config.tie_word_embeddings
+
+ super().__post_init__(**kwargs)
+
+
+__all__ = ["LlavaConfig"]
diff --git a/third_party/transformers/src/transformers/models/llava/convert_llava_weights_to_hf.py b/third_party/transformers/src/transformers/models/llava/convert_llava_weights_to_hf.py
new file mode 100644
index 0000000000000000000000000000000000000000..936e113b0b9b5d0c4d2404d85ce9383766d2ba97
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/llava/convert_llava_weights_to_hf.py
@@ -0,0 +1,202 @@
+# Copyright 2023 The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import argparse
+import glob
+
+import torch
+from huggingface_hub import file_exists, hf_hub_download, snapshot_download
+from safetensors import safe_open
+
+from transformers import (
+ AddedToken,
+ AutoConfig,
+ AutoImageProcessor,
+ AutoTokenizer,
+ LlavaConfig,
+ LlavaForConditionalGeneration,
+ LlavaProcessor,
+ SiglipVisionConfig,
+)
+
+
+EPILOG_TXT = """Example:
+ python transformers/src/transformers/models/llava/convert_llava_weights_to_hf.py --text_model_id lmsys/vicuna-7b-v1.5 --vision_model_id openai/clip-vit-large-patch14-336 --output_hub_path org/llava-v1.5-7b-conv --old_state_dict_id liuhaotian/llava-v1.5-7b
+
+Example for creating the old state dict file with Python:
+
+ import torch
+ from llava.model.language_model.llava_llama import LlavaLlamaForCausalLM
+
+ # load model
+ kwargs = {"device_map": "auto", "dtype": torch.float16}
+ model = LlavaLlamaForCausalLM.from_pretrained("liuhaotian/llava-v1.5-7b", **kwargs)
+
+ # load vision tower
+ model.get_vision_tower().load_model()
+
+ # Save state dict
+ torch.save(model.state_dict(), "tmp/hf_models/llava-v1.5-7b/model_state_dict.bin")
+"""
+
+KEYS_TO_MODIFY_MAPPING = {
+ "model.vision_tower.": "",
+ ".vision_resampler": "", # all lmms-lab models do avg pooling, so no vision_resampler
+ "model.mm_projector": "multi_modal_projector",
+ "model": "model.model",
+ "vision_model.model": "vision_model",
+ "lm_head": "language_model.lm_head",
+ "model.model": "language_model.model",
+ "multi_modal_projector.0": "multi_modal_projector.linear_1",
+ "multi_modal_projector.2": "multi_modal_projector.linear_2",
+}
+
+
+def load_original_state_dict(model_id):
+ directory_path = snapshot_download(repo_id=model_id, allow_patterns=["*.safetensors"])
+
+ original_state_dict = {}
+ for path in glob.glob(f"{directory_path}/*"):
+ if path.endswith(".safetensors"):
+ with safe_open(path, framework="pt", device="cpu") as f:
+ for key in f.keys():
+ original_state_dict[key] = f.get_tensor(key)
+
+ # tied weights so lm.head is not saved. Let's clone to load state dict
+ if "lm_head.weight" not in original_state_dict:
+ original_state_dict["lm_head.weight"] = original_state_dict["model.embed_tokens.weight"].clone()
+
+ if "model.image_newline" in original_state_dict:
+ # not used in the original implementation because "merge_type=flat"
+ del original_state_dict["model.image_newline"]
+ return original_state_dict
+
+
+# used only for llava-interlave
+# for ex: Qwen/Qwen1.5-0.5B-Chat google/siglip-so400m-patch14-384 lmms-lab/llava-next-interleave-qwen-0.5b
+def convert_state_dict_to_hf(state_dict):
+ new_state_dict = {}
+ for key, value in state_dict.items():
+ if key.endswith(".inv_freq"):
+ continue
+ for key_to_modify, new_key in KEYS_TO_MODIFY_MAPPING.items():
+ if key_to_modify in key:
+ key = key.replace(key_to_modify, new_key)
+
+ new_state_dict[key] = value
+ return new_state_dict
+
+
+def convert_llava_llama_to_hf(text_model_id, vision_model_id, output_hub_path, old_state_dict_id):
+ torch.set_default_dtype(torch.float16)
+ text_config = AutoConfig.from_pretrained(text_model_id)
+
+ tokenizer = AutoTokenizer.from_pretrained(text_model_id)
+ tokenizer.add_tokens(AddedToken("", special=True, normalized=False), special_tokens=True)
+ if "Qwen" not in text_model_id: # qwen already has a pad token
+ tokenizer.add_special_tokens({"pad_token": ""})
+
+ image_processor = AutoImageProcessor.from_pretrained(vision_model_id)
+ processor = LlavaProcessor(tokenizer=tokenizer, image_processor=image_processor)
+
+ if "siglip" in vision_model_id:
+ vision_config = SiglipVisionConfig(
+ hidden_size=1152,
+ image_size=384,
+ intermediate_size=4304,
+ num_attention_heads=16,
+ num_hidden_layers=26,
+ patch_size=14,
+ vision_use_head=False,
+ ).to_dict()
+ else:
+ vision_config = None
+
+ config = LlavaConfig(
+ text_config=text_config,
+ vision_config=vision_config,
+ )
+
+ # llms-lab interleave models do not use any selection strategy except for last hidden state
+ if "Qwen" in text_model_id:
+ config.image_token_id = 151646
+ if "siglip" in vision_model_id:
+ config.vision_feature_select_strategy = "full"
+ config.vision_feature_layer = -1
+ else:
+ config.pad_token_id = 32001
+ config.image_token_id = 32000
+
+ with torch.device("meta"):
+ model = LlavaForConditionalGeneration(config)
+
+ # Some llava variants like microsoft/llava-med-v1.5-mistral-7b use safetensors to store weights
+ if file_exists(old_state_dict_id, "model_state_dict.bin"):
+ state_dict_path = hf_hub_download(old_state_dict_id, "model_state_dict.bin")
+ state_dict = torch.load(state_dict_path, map_location="cpu", weights_only=True)
+ else:
+ state_dict = load_original_state_dict(old_state_dict_id)
+
+ state_dict = convert_state_dict_to_hf(state_dict)
+ model.load_state_dict(state_dict, strict=True, assign=True)
+
+ pre_expansion_embeddings = model.language_model.model.embed_tokens.weight.data
+ mu = torch.mean(pre_expansion_embeddings, dim=0).float()
+ n = pre_expansion_embeddings.size()[0]
+ sigma = ((pre_expansion_embeddings - mu).T @ (pre_expansion_embeddings - mu)) / n
+ dist = torch.distributions.multivariate_normal.MultivariateNormal(mu, covariance_matrix=1e-5 * sigma)
+
+ # We add an image token so we resize the model and pad to 64 for performance reasons
+ pad_shape = 64
+ vocab_size = config.text_config.vocab_size
+ model.resize_token_embeddings(config.text_config.vocab_size + 2, pad_shape)
+ model.language_model.model.embed_tokens.weight.data[vocab_size:] = torch.stack(
+ tuple(dist.sample() for _ in range(model.language_model.model.embed_tokens.weight.data[vocab_size:].shape[0])),
+ dim=0,
+ )
+ model.language_model.lm_head.weight.data[vocab_size:] = torch.stack(
+ tuple(dist.sample() for _ in range(model.language_model.lm_head.weight.data[vocab_size:].shape[0])),
+ dim=0,
+ )
+
+ model.push_to_hub(output_hub_path)
+ processor.push_to_hub(output_hub_path)
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ epilog=EPILOG_TXT,
+ formatter_class=argparse.RawDescriptionHelpFormatter,
+ )
+ parser.add_argument(
+ "--text_model_id",
+ help="Hub location of the text model",
+ )
+ parser.add_argument(
+ "--vision_model_id",
+ help="Hub location of the vision model",
+ )
+ parser.add_argument(
+ "--output_hub_path",
+ help="Location on the hub of the converted model",
+ )
+ parser.add_argument(
+ "--old_state_dict_id",
+ help="Location on the hub of the raw state dict of the original model. The filename needs to be `model_state_dict.bin`",
+ )
+ args = parser.parse_args()
+ convert_llava_llama_to_hf(args.text_model_id, args.vision_model_id, args.output_hub_path, args.old_state_dict_id)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/third_party/transformers/src/transformers/models/llava/image_processing_llava.py b/third_party/transformers/src/transformers/models/llava/image_processing_llava.py
new file mode 100644
index 0000000000000000000000000000000000000000..14d749e49ed3094541d1c6c0c461b20f4477e044
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/llava/image_processing_llava.py
@@ -0,0 +1,155 @@
+# Copyright 2024 The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Image processor class for LLaVa."""
+
+from typing import Union
+
+import torch
+from torchvision.transforms.v2 import functional as tvF
+
+from ...image_processing_backends import TorchvisionBackend
+from ...image_processing_utils import BatchFeature
+from ...image_transforms import (
+ group_images_by_shape,
+ reorder_images,
+)
+from ...image_utils import (
+ OPENAI_CLIP_MEAN,
+ OPENAI_CLIP_STD,
+ PILImageResampling,
+ SizeDict,
+)
+from ...processing_utils import ImagesKwargs, Unpack
+from ...utils import TensorType, auto_docstring
+
+
+@auto_docstring
+class LlavaImageProcessor(TorchvisionBackend):
+ resample = PILImageResampling.BICUBIC
+ image_mean = OPENAI_CLIP_MEAN
+ image_std = OPENAI_CLIP_STD
+ size = {"shortest_edge": 224}
+ default_to_square = False
+ crop_size = {"height": 224, "width": 224}
+ do_pad = False
+ do_resize = True
+ do_center_crop = True
+ do_rescale = True
+ do_normalize = True
+ do_convert_rgb = True
+
+ def __init__(self, **kwargs: Unpack[ImagesKwargs]):
+ super().__init__(**kwargs)
+
+ def pad_to_square(
+ self,
+ images: "torch.Tensor",
+ background_color: int | tuple[int, int, int] = 0,
+ ) -> "torch.Tensor":
+ """
+ Pads an image to a square based on the longest edge.
+
+ Args:
+ images (`torch.Tensor`):
+ The images to pad. Shape: (batch_size, num_channels, height, width) or (num_channels, height, width).
+ background_color (`int` or `tuple[int, int, int]`, *optional*, defaults to 0):
+ The color to use for the padding. Can be an integer for single channel or a
+ tuple of integers representing for multi-channel images. If passed as integer
+ in multi-channel mode, it will default to `0` in subsequent channels.
+ Returns:
+ `torch.Tensor`: The padded images.
+ """
+ height, width = images.shape[-2:]
+
+ if height == width:
+ return images
+
+ num_channels = images.shape[1] if len(images.shape) == 4 else images.shape[0]
+ if isinstance(background_color, int):
+ background_color = [background_color] + [0] * (num_channels - 1)
+ elif len(background_color) != num_channels:
+ raise ValueError(
+ f"background_color must have no more than {num_channels} elements to match the number of channels"
+ )
+
+ max_dim = max(height, width)
+ paste_x_left = (max_dim - width) // 2
+ paste_y_left = (max_dim - height) // 2
+ paste_x_right = max_dim - width - paste_x_left
+ paste_y_right = max_dim - height - paste_y_left
+ padded_images = tvF.pad(
+ images, padding=[paste_x_left, paste_y_left, paste_x_right, paste_y_right], fill=background_color
+ )
+
+ return padded_images
+
+ def _preprocess(
+ self,
+ images: list["torch.Tensor"],
+ do_resize: bool,
+ size: SizeDict,
+ resample: Union["PILImageResampling", "tvF.InterpolationMode", int] | None,
+ do_center_crop: bool,
+ crop_size: SizeDict,
+ do_rescale: bool,
+ rescale_factor: float,
+ do_normalize: bool,
+ image_mean: float | list[float] | None,
+ image_std: float | list[float] | None,
+ do_pad: bool | None,
+ pad_size: SizeDict | None,
+ disable_grouping: bool | None,
+ return_tensors: str | TensorType | None,
+ **kwargs,
+ ) -> BatchFeature:
+ # Group images by size for batched resizing
+ grouped_images, grouped_images_index = group_images_by_shape(images, disable_grouping=disable_grouping)
+ resized_images_grouped = {}
+ for shape, stacked_images in grouped_images.items():
+ if do_pad:
+ stacked_images = self.pad_to_square(
+ images=stacked_images, background_color=tuple(int(x * 255) for x in image_mean)
+ )
+ resized_images_grouped[shape] = stacked_images
+ padded_images = reorder_images(resized_images_grouped, grouped_images_index)
+
+ # Group images by size for batched resizing
+ # Needed in case do_pad is False, or padding returns images with different sizes
+ grouped_images, grouped_images_index = group_images_by_shape(padded_images, disable_grouping=disable_grouping)
+ resized_images_grouped = {}
+ for shape, stacked_images in grouped_images.items():
+ if do_resize:
+ stacked_images = self.resize(image=stacked_images, size=size, resample=resample)
+ resized_images_grouped[shape] = stacked_images
+ resized_images = reorder_images(resized_images_grouped, grouped_images_index)
+
+ # Group images by size for further processing
+ # Needed in case do_resize is False, or resize returns images with different sizes
+ grouped_images, grouped_images_index = group_images_by_shape(resized_images, disable_grouping=disable_grouping)
+ processed_images_grouped = {}
+ for shape, stacked_images in grouped_images.items():
+ if do_center_crop:
+ stacked_images = self.center_crop(stacked_images, crop_size)
+ # Fused rescale and normalize
+ stacked_images = self.rescale_and_normalize(
+ stacked_images, do_rescale, rescale_factor, do_normalize, image_mean, image_std
+ )
+ processed_images_grouped[shape] = stacked_images
+
+ processed_images = reorder_images(processed_images_grouped, grouped_images_index)
+
+ return BatchFeature(data={"pixel_values": processed_images}, tensor_type=return_tensors)
+
+
+__all__ = ["LlavaImageProcessor"]
diff --git a/third_party/transformers/src/transformers/models/llava/image_processing_pil_llava.py b/third_party/transformers/src/transformers/models/llava/image_processing_pil_llava.py
new file mode 100644
index 0000000000000000000000000000000000000000..d953f45b8c9702baa4cc665b9fc5bda5aedcfd27
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/llava/image_processing_pil_llava.py
@@ -0,0 +1,135 @@
+# Copyright 2024 The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Image processor class for LLaVa."""
+
+import numpy as np
+
+from ...image_processing_backends import PilBackend
+from ...image_processing_utils import BatchFeature
+from ...image_utils import (
+ OPENAI_CLIP_MEAN,
+ OPENAI_CLIP_STD,
+ PILImageResampling,
+ SizeDict,
+)
+from ...processing_utils import ImagesKwargs, Unpack
+from ...utils import TensorType, auto_docstring
+
+
+@auto_docstring
+class LlavaImageProcessorPil(PilBackend):
+ resample = PILImageResampling.BICUBIC
+ image_mean = OPENAI_CLIP_MEAN
+ image_std = OPENAI_CLIP_STD
+ size = {"shortest_edge": 224}
+ default_to_square = False
+ crop_size = {"height": 224, "width": 224}
+ do_pad = False
+ do_resize = True
+ do_center_crop = True
+ do_rescale = True
+ do_normalize = True
+ do_convert_rgb = True
+
+ def __init__(self, **kwargs: Unpack[ImagesKwargs]):
+ super().__init__(**kwargs)
+
+ def pad_to_square(
+ self,
+ image: np.ndarray,
+ background_color: int | tuple[int, int, int] = 0,
+ ) -> np.ndarray:
+ """
+ Pads an image to a square based on the longest edge.
+
+ Args:
+ image (`np.ndarray`):
+ The image to pad. Shape: (num_channels, height, width) - always channels_first in backend.
+ background_color (`int` or `tuple[int, int, int]`, *optional*, defaults to 0):
+ The color to use for the padding.
+
+ Returns:
+ `np.ndarray`: The padded image.
+ """
+ # Backend always uses channels_first format: (num_channels, height, width)
+ num_channels, height, width = image.shape
+
+ if height == width:
+ return image
+
+ max_dim = max(height, width)
+
+ # Ensure background_color is the correct shape
+ if isinstance(background_color, int):
+ background_color = [background_color]
+ elif len(background_color) != num_channels:
+ raise ValueError(
+ f"background_color must have no more than {num_channels} elements to match the number of channels"
+ )
+
+ result = np.zeros((num_channels, max_dim, max_dim), dtype=image.dtype)
+ for i, color in enumerate(background_color):
+ result[i, :, :] = color
+ if width > height:
+ start = (max_dim - height) // 2
+ result[:, start : start + height, :] = image
+ else:
+ start = (max_dim - width) // 2
+ result[:, :, start : start + width] = image
+
+ return result
+
+ def _preprocess(
+ self,
+ images: list[np.ndarray],
+ do_resize: bool,
+ size: SizeDict,
+ resample: "PILImageResampling | None",
+ do_center_crop: bool,
+ crop_size: SizeDict,
+ do_rescale: bool,
+ rescale_factor: float,
+ do_normalize: bool,
+ image_mean: float | list[float] | None,
+ image_std: float | list[float] | None,
+ do_pad: bool | None,
+ pad_size: SizeDict | None,
+ return_tensors: str | TensorType | None,
+ **kwargs,
+ ) -> BatchFeature:
+ processed_images = []
+ for image in images:
+ # Apply pad_to_square first if needed (before resize)
+ if do_pad:
+ background_color = tuple(int(x * 255) for x in image_mean) if image_mean else 0
+ image = self.pad_to_square(image, background_color=background_color)
+
+ if do_resize:
+ image = self.resize(image=image, size=size, resample=resample)
+
+ if do_center_crop:
+ image = self.center_crop(image, crop_size)
+
+ if do_rescale:
+ image = self.rescale(image, rescale_factor)
+
+ if do_normalize:
+ image = self.normalize(image, image_mean, image_std)
+
+ processed_images.append(image)
+
+ return BatchFeature(data={"pixel_values": processed_images}, tensor_type=return_tensors)
+
+
+__all__ = ["LlavaImageProcessorPil"]
diff --git a/third_party/transformers/src/transformers/models/llava/modeling_llava.py b/third_party/transformers/src/transformers/models/llava/modeling_llava.py
new file mode 100644
index 0000000000000000000000000000000000000000..f17041dca72befb36d25006a6b0b96dc4adca385
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/llava/modeling_llava.py
@@ -0,0 +1,423 @@
+# Copyright 2023 the HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""PyTorch Llava model."""
+
+from dataclasses import dataclass
+
+import torch
+from torch import nn
+
+from ...activations import ACT2FN
+from ...cache_utils import Cache
+from ...generation import GenerationMixin
+from ...modeling_outputs import BaseModelOutputWithPast, BaseModelOutputWithPooling, ModelOutput
+from ...modeling_utils import PreTrainedModel
+from ...processing_utils import Unpack
+from ...utils import TransformersKwargs, auto_docstring, logging, torch_compilable_check
+from ...utils.generic import can_return_tuple, merge_with_config_defaults
+from ..auto import AutoModel
+from .configuration_llava import LlavaConfig
+
+
+logger = logging.get_logger(__name__)
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ Base class for Llava outputs, with hidden states and attentions.
+ """
+)
+class LlavaModelOutputWithPast(BaseModelOutputWithPast):
+ r"""
+ past_key_values (`Cache`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
+ It is a [`~cache_utils.Cache`] instance. For more details, see our [kv cache guide](https://huggingface.co/docs/transformers/en/kv_cache).
+
+ Contains pre-computed hidden-states (key and values in the self-attention blocks) that can be used (see
+ `past_key_values` input) to speed up sequential decoding.
+ image_hidden_states (`torch.FloatTensor`, *optional*):
+ A `torch.FloatTensor` of size `(batch_size, num_images, sequence_length, hidden_size)`.
+ image_hidden_states of the model produced by the vision encoder and after projecting the last hidden state.
+ """
+
+ image_hidden_states: torch.FloatTensor | None = None
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ Base class for Llava causal language model (or autoregressive) outputs.
+ """
+)
+class LlavaCausalLMOutputWithPast(ModelOutput):
+ r"""
+ loss (`torch.FloatTensor` of shape `(1,)`, *optional*, returned when `labels` is provided):
+ Language modeling loss (for next-token prediction).
+ logits (`torch.FloatTensor` of shape `(batch_size, sequence_length, config.vocab_size)`):
+ Prediction scores of the language modeling head (scores for each vocabulary token before SoftMax).
+ past_key_values (`Cache`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
+ It is a [`~cache_utils.Cache`] instance. For more details, see our [kv cache guide](https://huggingface.co/docs/transformers/en/kv_cache).
+
+ Contains pre-computed hidden-states (key and values in the self-attention blocks) that can be used (see
+ `past_key_values` input) to speed up sequential decoding.
+ image_hidden_states (`torch.FloatTensor`, *optional*):
+ A `torch.FloatTensor` of size `(batch_size, num_images, sequence_length, hidden_size)`.
+ image_hidden_states of the model produced by the vision encoder and after projecting the last hidden state.
+ """
+
+ loss: torch.FloatTensor | None = None
+ logits: torch.FloatTensor | None = None
+ past_key_values: Cache | None = None
+ hidden_states: tuple[torch.FloatTensor] | None = None
+ attentions: tuple[torch.FloatTensor] | None = None
+ image_hidden_states: torch.FloatTensor | None = None
+
+
+class LlavaMultiModalProjector(nn.Module):
+ def __init__(self, config: LlavaConfig):
+ super().__init__()
+ # We have hidden_size * the number of vision feature layers
+ num_feature_layers = 1 if isinstance(config.vision_feature_layer, int) else len(config.vision_feature_layer)
+ self.linear_1 = nn.Linear(
+ config.vision_config.hidden_size * num_feature_layers,
+ config.text_config.hidden_size,
+ bias=config.multimodal_projector_bias,
+ )
+ self.act = ACT2FN[config.projector_hidden_act]
+ self.linear_2 = nn.Linear(
+ config.text_config.hidden_size, config.text_config.hidden_size, bias=config.multimodal_projector_bias
+ )
+
+ def forward(self, image_features):
+ hidden_states = self.linear_1(image_features)
+ hidden_states = self.act(hidden_states)
+ hidden_states = self.linear_2(hidden_states)
+ return hidden_states
+
+
+@auto_docstring
+class LlavaPreTrainedModel(PreTrainedModel):
+ config: LlavaConfig
+ base_model_prefix = "model"
+ input_modalities = ("image", "text")
+ supports_gradient_checkpointing = True
+ _skip_keys_device_placement = "past_key_values"
+
+ _supports_flash_attn = True
+ _supports_sdpa = True
+
+ _can_compile_fullgraph = True
+ _supports_flex_attn = True
+ _supports_attention_backend = True
+
+
+@auto_docstring(
+ custom_intro="""
+ The Llava model which consists of a vision backbone and a language model, without a language modeling head.
+ """
+)
+class LlavaModel(LlavaPreTrainedModel):
+ def __init__(self, config: LlavaConfig):
+ super().__init__(config)
+ self.vision_tower = AutoModel.from_config(config.vision_config)
+
+ self.multi_modal_projector = LlavaMultiModalProjector(config)
+ self.language_model = AutoModel.from_config(config.text_config)
+ self.post_init()
+
+ def get_input_embeddings(self):
+ return self.language_model.get_input_embeddings()
+
+ def set_input_embeddings(self, value):
+ self.language_model.set_input_embeddings(value)
+
+ @merge_with_config_defaults
+ @can_return_tuple
+ @auto_docstring(
+ custom_intro="Obtains image last hidden states from the vision tower and apply multimodal projection."
+ )
+ def get_image_features(
+ self,
+ pixel_values: torch.FloatTensor,
+ vision_feature_layer: int | list[int] | list[int] | None = None,
+ vision_feature_select_strategy: str | None = None,
+ output_hidden_states: bool | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | BaseModelOutputWithPooling:
+ kwargs = {k: v for k, v in kwargs.items() if v is not None}
+ # this is not memory efficient at all (output_hidden_states=True) will save all the hidden states.
+ image_outputs = self.vision_tower(
+ pixel_values,
+ output_hidden_states=True, # Ignore arg on purpose
+ return_dict=True,
+ **kwargs,
+ )
+
+ # If we have one vision feature layer, return the corresponding hidden states,
+ # otherwise, select the hidden states of each feature layer and concatenate them
+ if isinstance(vision_feature_layer, int):
+ selected_image_feature = image_outputs.hidden_states[vision_feature_layer]
+ if vision_feature_select_strategy == "default":
+ selected_image_feature = selected_image_feature[:, 1:]
+ else:
+ hs_pool = [image_outputs.hidden_states[layer_idx] for layer_idx in vision_feature_layer]
+ # For default; crop CLS from each hidden state in the hidden state pool
+ if vision_feature_select_strategy == "default":
+ hs_pool = [hs[:, 1:] for hs in hs_pool]
+ selected_image_feature = torch.cat(hs_pool, dim=-1)
+
+ image_features = self.multi_modal_projector(selected_image_feature)
+
+ # If image_sizes is provided, we need to split the image features accordingly,
+ # but only if the image_sizes is not None (the default in this and related architectures)
+ if kwargs.get("image_sizes") is not None:
+ split_sizes = (
+ (torch.as_tensor(kwargs["image_sizes"], device=image_features.device) // self.vision_tower.patch_size)
+ .prod(dim=-1)
+ .tolist()
+ )
+ image_features = torch.split(image_features.squeeze(0), split_sizes)
+ else:
+ image_features = list(image_features)
+ image_outputs.pooler_output = image_features
+
+ return image_outputs
+
+ def get_placeholder_mask(
+ self, input_ids: torch.LongTensor, inputs_embeds: torch.FloatTensor, image_features: torch.FloatTensor
+ ):
+ """
+ Obtains multimodal placeholder mask from `input_ids` or `inputs_embeds`, and checks that the placeholder token count is
+ equal to the length of multimodal features. If the lengths are different, an error is raised.
+ """
+ if input_ids is None:
+ special_image_mask = inputs_embeds == self.get_input_embeddings()(
+ torch.tensor(self.config.image_token_id, dtype=torch.long, device=inputs_embeds.device)
+ )
+ special_image_mask = special_image_mask.all(-1)
+ else:
+ special_image_mask = input_ids == self.config.image_token_id
+
+ n_image_tokens = special_image_mask.sum()
+ n_image_features = image_features.shape[0] * image_features.shape[1]
+ special_image_mask = special_image_mask.unsqueeze(-1).expand_as(inputs_embeds).to(inputs_embeds.device)
+ torch_compilable_check(
+ inputs_embeds[special_image_mask].numel() == image_features.numel(),
+ f"Image features and image tokens do not match, tokens: {n_image_tokens}, features: {n_image_features}",
+ )
+ return special_image_mask
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ pixel_values: torch.FloatTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ vision_feature_layer: int | list[int] | list[int] | None = None,
+ vision_feature_select_strategy: str | None = None,
+ image_sizes: torch.Tensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | LlavaModelOutputWithPast:
+ 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 None:
+ inputs_embeds = self.get_input_embeddings()(input_ids)
+
+ if pixel_values is not None:
+ image_features = self.get_image_features(
+ pixel_values=pixel_values,
+ vision_feature_layer=vision_feature_layer,
+ vision_feature_select_strategy=vision_feature_select_strategy,
+ image_sizes=image_sizes,
+ return_dict=True,
+ ).pooler_output
+ image_features = torch.cat(image_features, dim=0).to(inputs_embeds.device, inputs_embeds.dtype)
+ special_image_mask = self.get_placeholder_mask(
+ input_ids, inputs_embeds=inputs_embeds, image_features=image_features
+ )
+ inputs_embeds = inputs_embeds.masked_scatter(special_image_mask, image_features)
+
+ outputs = self.language_model(
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ **kwargs,
+ )
+
+ return LlavaModelOutputWithPast(
+ last_hidden_state=outputs.last_hidden_state,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ image_hidden_states=image_features if pixel_values is not None else None,
+ )
+
+
+@auto_docstring(
+ custom_intro="""
+ The LLAVA model which consists of a vision backbone and a language model.
+ """
+)
+class LlavaForConditionalGeneration(LlavaPreTrainedModel, GenerationMixin):
+ _tied_weights_keys = {"lm_head.weight": "model.language_model.embed_tokens.weight"}
+
+ def __init__(self, config: LlavaConfig):
+ super().__init__(config)
+ self.model = LlavaModel(config)
+ self.lm_head = nn.Linear(config.text_config.hidden_size, config.text_config.vocab_size, bias=False)
+ self.post_init()
+
+ def get_input_embeddings(self):
+ return self.model.get_input_embeddings()
+
+ def set_input_embeddings(self, value):
+ self.model.set_input_embeddings(value)
+
+ def get_output_embeddings(self) -> nn.Module:
+ return self.lm_head
+
+ @auto_docstring
+ def get_image_features(
+ self,
+ pixel_values: torch.FloatTensor,
+ vision_feature_layer: int | list[int] | list[int] | None = None,
+ vision_feature_select_strategy: str | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | BaseModelOutputWithPooling:
+ return self.model.get_image_features(
+ pixel_values=pixel_values,
+ vision_feature_layer=vision_feature_layer,
+ vision_feature_select_strategy=vision_feature_select_strategy,
+ **kwargs,
+ )
+
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ pixel_values: torch.FloatTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ vision_feature_layer: int | list[int] | list[int] | None = None,
+ vision_feature_select_strategy: str | None = None,
+ labels: torch.LongTensor | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ image_sizes: torch.Tensor | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | LlavaCausalLMOutputWithPast:
+ r"""
+ 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]`.
+
+ Example:
+
+ ```python
+ >>> from PIL import Image
+ >>> import httpx
+ >>> from io import BytesIO
+ >>> from transformers import AutoProcessor, LlavaForConditionalGeneration
+
+ >>> model = LlavaForConditionalGeneration.from_pretrained("llava-hf/llava-1.5-7b-hf")
+ >>> processor = AutoProcessor.from_pretrained("llava-hf/llava-1.5-7b-hf")
+
+ >>> prompt = "USER: \nWhat's the content of the image? ASSISTANT:"
+ >>> url = "https://www.ilankelman.org/stopsigns/australia.jpg"
+ >>> with httpx.stream("GET", url) as response:
+ ... image = Image.open(BytesIO(response.read()))
+
+ >>> inputs = processor(images=image, text=prompt, return_tensors="pt")
+
+ >>> # Generate
+ >>> generate_ids = model.generate(**inputs, max_new_tokens=15)
+ >>> processor.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]
+ "USER: \nWhat's the content of the image? ASSISTANT: The image features a busy city street with a stop sign prominently displayed"
+ ```"""
+ outputs = self.model(
+ input_ids=input_ids,
+ pixel_values=pixel_values,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ vision_feature_layer=vision_feature_layer,
+ vision_feature_select_strategy=vision_feature_select_strategy,
+ image_sizes=image_sizes,
+ **kwargs,
+ )
+
+ hidden_states = outputs[0]
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(
+ logits=logits, labels=labels, vocab_size=self.config.text_config.vocab_size, **kwargs
+ )
+
+ return LlavaCausalLMOutputWithPast(
+ loss=loss,
+ logits=logits,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ image_hidden_states=outputs.image_hidden_states,
+ )
+
+ def prepare_inputs_for_generation(
+ self,
+ input_ids,
+ past_key_values=None,
+ inputs_embeds=None,
+ pixel_values=None,
+ attention_mask=None,
+ logits_to_keep=None,
+ is_first_iteration=False,
+ **kwargs,
+ ):
+ # Overwritten -- in specific circumstances we don't want to forward image inputs to the model
+
+ model_inputs = super().prepare_inputs_for_generation(
+ input_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ attention_mask=attention_mask,
+ logits_to_keep=logits_to_keep,
+ is_first_iteration=is_first_iteration,
+ **kwargs,
+ )
+
+ if is_first_iteration or not kwargs.get("use_cache", True):
+ # Pixel values are used only in the first iteration if available
+ # In subsequent iterations, they are already merged with text and cached
+ # NOTE: first iteration doesn't have to be prefill, it can be the first
+ # iteration with a question and cached system prompt (continue generate from cache)
+ model_inputs["pixel_values"] = pixel_values
+
+ return model_inputs
+
+
+__all__ = ["LlavaForConditionalGeneration", "LlavaPreTrainedModel", "LlavaModel"]
diff --git a/third_party/transformers/src/transformers/models/llava/processing_llava.py b/third_party/transformers/src/transformers/models/llava/processing_llava.py
new file mode 100644
index 0000000000000000000000000000000000000000..f219e5208caa09edf429054fec4c790eec2f5d4b
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/llava/processing_llava.py
@@ -0,0 +1,164 @@
+# Copyright 2023 The HuggingFace Inc. team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Processor class for Llava.
+"""
+
+from ...feature_extraction_utils import BatchFeature
+from ...image_utils import ImageInput, get_image_size, to_numpy_array
+from ...processing_utils import (
+ MultiModalData,
+ ProcessingKwargs,
+ ProcessorMixin,
+ Unpack,
+)
+from ...tokenization_utils_base import PreTokenizedInput, TextInput
+from ...utils import auto_docstring, logging
+
+
+logger = logging.get_logger(__name__)
+
+
+class LlavaProcessorKwargs(ProcessingKwargs, total=False):
+ _defaults = {
+ "text_kwargs": {"padding": False, "return_mm_token_type_ids": False},
+ }
+
+
+@auto_docstring
+class LlavaProcessor(ProcessorMixin):
+ def __init__(
+ self,
+ image_processor=None,
+ tokenizer=None,
+ patch_size=None,
+ vision_feature_select_strategy=None,
+ chat_template=None,
+ image_token="", # set the default and let users change if they have peculiar special tokens in rare cases
+ num_additional_image_tokens=0,
+ **kwargs,
+ ):
+ r"""
+ patch_size (`int`, *optional*):
+ Patch size from the vision tower.
+ vision_feature_select_strategy (`str`, *optional*):
+ The feature selection strategy used to select the vision feature from the vision backbone.
+ Should be same as in model's config
+ image_token (`str`, *optional*, defaults to `""`):
+ Special token used to denote image location.
+ num_additional_image_tokens (`int`, *optional*, defaults to 0):
+ Number of additional tokens added to the image embeddings, such as CLS (+1). If the backbone has no CLS or other
+ extra tokens appended, no need to set this arg.
+ """
+ self.patch_size = patch_size
+ self.num_additional_image_tokens = num_additional_image_tokens
+ self.vision_feature_select_strategy = vision_feature_select_strategy
+ self.image_token = tokenizer.image_token if hasattr(tokenizer, "image_token") else image_token
+ self.image_token_id = tokenizer.encode(self.image_token, add_special_tokens=False)[0]
+ super().__init__(image_processor, tokenizer, chat_template=chat_template)
+
+ @auto_docstring
+ def __call__(
+ self,
+ images: ImageInput | None = None,
+ text: TextInput | PreTokenizedInput | list[TextInput] | list[PreTokenizedInput] = None,
+ **kwargs: Unpack[LlavaProcessorKwargs],
+ ) -> BatchFeature:
+ r"""
+ Returns:
+ [`BatchFeature`]: A [`BatchFeature`] with the following fields:
+
+ - **input_ids** -- List of token ids to be fed to a model. Returned when `text` is not `None`.
+ - **attention_mask** -- List of indices specifying which tokens should be attended to by the model (when
+ `return_attention_mask=True` or if *"attention_mask"* is in `self.model_input_names` and if `text` is not
+ `None`).
+ - **pixel_values** -- Pixel values to be fed to a model. Returned when `images` is not `None`.
+ """
+ if images is None and text is None:
+ raise ValueError("You have to specify at least one of `images` or `text`.")
+
+ output_kwargs = self._merge_kwargs(
+ LlavaProcessorKwargs,
+ tokenizer_init_kwargs=self.tokenizer.init_kwargs,
+ **kwargs,
+ )
+ if images is not None:
+ image_inputs = self.image_processor(images, **output_kwargs["images_kwargs"])
+ else:
+ image_inputs = {}
+
+ if isinstance(text, str):
+ text = [text]
+ elif not isinstance(text, list) and not isinstance(text[0], str):
+ raise TypeError("Invalid input text. Please provide a string, or a list of strings")
+
+ # try to expand inputs in processing if we have the necessary parts
+ prompt_strings = text
+ if image_inputs.get("pixel_values") is not None:
+ # Replace the image token with the expanded image token sequence
+ pixel_values = image_inputs["pixel_values"]
+ height, width = get_image_size(to_numpy_array(pixel_values[0]))
+ num_image_tokens = (height // self.patch_size) * (
+ width // self.patch_size
+ ) + self.num_additional_image_tokens
+ if self.vision_feature_select_strategy == "default":
+ num_image_tokens -= 1
+
+ prompt_strings = []
+ for sample in text:
+ sample = sample.replace(self.image_token, self.image_token * num_image_tokens)
+ prompt_strings.append(sample)
+
+ return_tensors = output_kwargs["text_kwargs"].pop("return_tensors", None)
+ return_mm_token_type_ids = output_kwargs["text_kwargs"].pop("return_mm_token_type_ids", False)
+ text_inputs = self.tokenizer(prompt_strings, **output_kwargs["text_kwargs"], return_tensors=None)
+ self._check_special_mm_tokens(prompt_strings, text_inputs, modalities=["image"])
+
+ if return_mm_token_type_ids:
+ text_inputs["mm_token_type_ids"] = self.create_mm_token_type_ids(text_inputs["input_ids"])
+ return BatchFeature(data={**text_inputs, **image_inputs}, tensor_type=return_tensors)
+
+ def _get_num_multimodal_tokens(self, image_sizes=None, **kwargs):
+ """
+ Computes the number of placeholder tokens needed for multimodal inputs with the given sizes.
+
+ Args:
+ image_sizes (`list[list[int]]`, *optional*):
+ The input sizes formatted as (height, width) per each image.
+
+ Returns:
+ `MultiModalData`: A `MultiModalData` object holding number of tokens per each of the provided
+ input modalities, along with other useful data.
+ """
+
+ vision_data = {}
+ if image_sizes is not None:
+ images_kwargs = LlavaProcessorKwargs._defaults.get("images_kwargs", {})
+ images_kwargs.update(kwargs)
+ crop_size = images_kwargs.get("crop_size", None) or self.image_processor.crop_size
+ resized_height, resized_width = crop_size["height"], crop_size["width"]
+
+ num_image_tokens = (resized_height // self.patch_size) * (resized_width // self.patch_size)
+ num_image_tokens += self.num_additional_image_tokens
+ if self.vision_feature_select_strategy == "default":
+ num_image_tokens -= 1
+
+ num_image_tokens = [num_image_tokens] * len(image_sizes)
+ num_image_patches = [1] * len(image_sizes)
+ vision_data.update({"num_image_tokens": num_image_tokens, "num_image_patches": num_image_patches})
+
+ return MultiModalData(**vision_data)
+
+
+__all__ = ["LlavaProcessor"]
diff --git a/third_party/transformers/src/transformers/models/llava_next/__init__.py b/third_party/transformers/src/transformers/models/llava_next/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..cb86e92317f86cf426b7d2934e71939f80800612
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/llava_next/__init__.py
@@ -0,0 +1,30 @@
+# Copyright 2024 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_llava_next import *
+ from .image_processing_llava_next import *
+ from .image_processing_pil_llava_next import *
+ from .modeling_llava_next import *
+ from .processing_llava_next import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/llava_next/configuration_llava_next.py b/third_party/transformers/src/transformers/models/llava_next/configuration_llava_next.py
new file mode 100644
index 0000000000000000000000000000000000000000..caef7eb8fd6af83bd0646d6fb1a6b6be7bebcc16
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/llava_next/configuration_llava_next.py
@@ -0,0 +1,99 @@
+# Copyright 2024 The HuggingFace Inc. team. All rights reserved.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Llava-NeXT model configuration"""
+
+from typing import Literal
+
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...utils import auto_docstring
+from ..auto import CONFIG_MAPPING, AutoConfig
+
+
+@auto_docstring(checkpoint="llava-hf/llava-v1.6-mistral-7b-hf")
+@strict
+class LlavaNextConfig(PreTrainedConfig):
+ r"""
+ image_grid_pinpoints (`List`, *optional*, defaults to `[[336, 672], [672, 336], [672, 672], [1008, 336], [336, 1008]]`):
+ A list of possible resolutions to use for processing high resolution images. Each item in the list should be a tuple or list
+ of the form `(height, width)`.
+
+ Example:
+
+ ```python
+ >>> from transformers import LlavaNextForConditionalGeneration, LlavaNextConfig, CLIPVisionConfig, LlamaConfig
+
+ >>> # Initializing a CLIP-vision config
+ >>> vision_config = CLIPVisionConfig()
+
+ >>> # Initializing a Llama config
+ >>> text_config = LlamaConfig()
+
+ >>> # Initializing a Llava-Next llava-hf/llava-v1.6-mistral-7b-hf style configuration
+ >>> configuration = LlavaNextConfig(vision_config, text_config)
+
+ >>> # Initializing a model from the llava-hf/llava-v1.6-mistral-7b-hf style configuration
+ >>> model = LlavaNextForConditionalGeneration(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "llava_next"
+ attribute_map = {"image_token_id": "image_token_index"}
+ sub_configs = {"text_config": AutoConfig, "vision_config": AutoConfig}
+
+ vision_config: dict | PreTrainedConfig | None = None
+ text_config: dict | PreTrainedConfig | None = None
+ image_token_index: int = 32000
+ projector_hidden_act: str = "gelu"
+ vision_feature_select_strategy: Literal["default", "full"] = "default"
+ vision_feature_layer: int | list[int] = -2
+ multimodal_projector_bias: bool = True
+ tie_word_embeddings: bool = False
+ image_grid_pinpoints: list | None = None
+ image_seq_length: int = 576
+
+ def __post_init__(self, **kwargs):
+ if isinstance(self.vision_config, dict):
+ self.vision_config["model_type"] = self.vision_config.get("model_type", "clip_vision_model")
+ self.vision_config = CONFIG_MAPPING[self.vision_config["model_type"]](**self.vision_config)
+ elif self.vision_config is None:
+ self.vision_config = CONFIG_MAPPING["clip_vision_model"](
+ intermediate_size=4096,
+ hidden_size=1024,
+ patch_size=14,
+ image_size=336,
+ num_hidden_layers=24,
+ num_attention_heads=16,
+ vocab_size=32000,
+ projection_dim=768,
+ )
+
+ if isinstance(self.text_config, dict):
+ self.text_config["model_type"] = self.text_config.get("model_type", "llama")
+ self.text_config = CONFIG_MAPPING[self.text_config["model_type"]](**self.text_config)
+ elif self.text_config is None:
+ self.text_config = CONFIG_MAPPING["llama"]()
+
+ self.image_grid_pinpoints = (
+ self.image_grid_pinpoints
+ if self.image_grid_pinpoints is not None
+ else [[336, 672], [672, 336], [672, 672], [1008, 336], [336, 1008]]
+ )
+
+ super().__post_init__(**kwargs)
+
+
+__all__ = ["LlavaNextConfig"]
diff --git a/third_party/transformers/src/transformers/models/llava_next/convert_llava_next_weights_to_hf.py b/third_party/transformers/src/transformers/models/llava_next/convert_llava_next_weights_to_hf.py
new file mode 100644
index 0000000000000000000000000000000000000000..25f9eb30508b6cd2f069fe63ac3bb493543723ac
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/llava_next/convert_llava_next_weights_to_hf.py
@@ -0,0 +1,398 @@
+# Copyright 2024 The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Convert LLaVa-NeXT (LLaVa-1.6) checkpoints from the original repository.
+
+URL: https://github.com/haotian-liu/LLaVA/tree/main.
+
+
+The command used to obtain original logits is the following:
+python llava/eval/run_llava.py --model-path "liuhaotian/llava-v1.6-mistral-7b" --image-file "images/llava_v1_5_radar.jpg" --query "What is shown in this image?" --max_new_tokens 100 --temperature 0
+
+Note: logits are tested with torch==2.1.2.
+"""
+
+import argparse
+import gc
+import glob
+import json
+from io import BytesIO
+from pathlib import Path
+
+import httpx
+import torch
+from huggingface_hub import hf_hub_download, snapshot_download
+from PIL import Image
+from safetensors import safe_open
+
+from transformers import (
+ AddedToken,
+ AutoConfig,
+ AutoTokenizer,
+ LlavaNextConfig,
+ LlavaNextForConditionalGeneration,
+ LlavaNextImageProcessor,
+ LlavaNextProcessor,
+)
+
+
+KEYS_TO_MODIFY_MAPPING = {
+ "model.vision_tower.": "",
+ "model.mm_projector": "multi_modal_projector",
+ "model": "model.model",
+ "vision_model.model": "vision_model",
+ "lm_head": "language_model.lm_head",
+ "model.model": "language_model.model",
+ "multi_modal_projector.0": "multi_modal_projector.linear_1",
+ "multi_modal_projector.2": "multi_modal_projector.linear_2",
+ "language_model.model.image_newline": "image_newline",
+}
+
+
+def load_original_state_dict(model_id):
+ directory_path = snapshot_download(repo_id=model_id, allow_patterns=["*.safetensors"])
+
+ original_state_dict = {}
+ for path in glob.glob(f"{directory_path}/*"):
+ if path.endswith(".safetensors"):
+ with safe_open(path, framework="pt", device="cpu") as f:
+ for key in f.keys():
+ original_state_dict[key] = f.get_tensor(key)
+
+ return original_state_dict
+
+
+def convert_state_dict_to_hf(state_dict):
+ new_state_dict = {}
+ for key, value in state_dict.items():
+ if key.endswith(".inv_freq"):
+ continue
+ for key_to_modify, new_key in KEYS_TO_MODIFY_MAPPING.items():
+ if key_to_modify in key:
+ key = key.replace(key_to_modify, new_key)
+
+ new_state_dict[key] = value.to(torch.float16)
+ return new_state_dict
+
+
+def load_image():
+ url = "https://github.com/haotian-liu/LLaVA/blob/1a91fc274d7c35a9b50b3cb29c4247ae5837ce39/images/llava_v1_5_radar.jpg?raw=true"
+ with httpx.stream("GET", url) as response:
+ image = Image.open(BytesIO(response.read()))
+ return image
+
+
+def convert_llava_to_hf(model_id, pytorch_dump_folder_path, push_to_hub=False):
+ # load original config
+ filepath = hf_hub_download(repo_id=model_id, filename="config.json", repo_type="model")
+ # read json
+ with open(filepath) as f:
+ data = json.load(f)
+ print(data)
+
+ if model_id == "liuhaotian/llava-v1.6-mistral-7b":
+ text_model_id = "mistralai/Mistral-7B-Instruct-v0.2"
+ image_token_id = 32000
+ elif model_id == "liuhaotian/llava-v1.6-vicuna-7b":
+ text_model_id = "lmsys/vicuna-7b-v1.5"
+ image_token_id = 32000
+ elif model_id == "liuhaotian/llava-v1.6-vicuna-13b":
+ text_model_id = "lmsys/vicuna-13b-v1.5"
+ image_token_id = 32000
+ elif model_id == "liuhaotian/llava-v1.6-34b":
+ text_model_id = "NousResearch/Nous-Hermes-2-Yi-34B"
+ image_token_id = 64000
+ elif model_id == "lmms-lab/llama3-llava-next-8b":
+ text_model_id = "meta-llama/Meta-Llama-3-8B-Instruct"
+ image_token_id = 128256
+ elif model_id == "lmms-lab/llava-next-72b":
+ text_model_id = "Qwen/Qwen1.5-72B-Chat"
+ image_token_id = 151646
+ elif model_id == "lmms-lab/llava-next-110b":
+ text_model_id = "Qwen/Qwen1.5-110B-Chat"
+ image_token_id = 151646
+
+ vision_model_id = data["mm_vision_tower"]
+
+ torch.set_default_dtype(torch.float16)
+ text_config = AutoConfig.from_pretrained(text_model_id)
+
+ use_fast = model_id != "liuhaotian/llava-v1.6-34b"
+ tokenizer = AutoTokenizer.from_pretrained(text_model_id, use_fast=use_fast)
+ tokenizer.add_tokens(AddedToken("", special=True, normalized=False), special_tokens=True)
+
+ if model_id in ("liuhaotian/llava-v1.6-mistral-7b", "lmms-lab/llama3-llava-next-8b"):
+ # Mistral-7B doesn't have a padding token set yet
+ tokenizer.add_special_tokens({"pad_token": ""})
+
+ image_processor = LlavaNextImageProcessor.from_pretrained(vision_model_id)
+ processor = LlavaNextProcessor(tokenizer=tokenizer, image_processor=image_processor)
+
+ config = LlavaNextConfig(
+ text_config=text_config.to_dict(),
+ image_grid_pinpoints=image_processor.image_grid_pinpoints,
+ use_image_newline_parameter=True,
+ image_token_id=image_token_id,
+ )
+
+ with torch.device("meta"):
+ model = LlavaNextForConditionalGeneration(config)
+
+ # load original state dict
+ state_dict = load_original_state_dict(model_id)
+ state_dict = convert_state_dict_to_hf(state_dict)
+ model.load_state_dict(state_dict, assign=True)
+ model.eval()
+
+ pre_expansion_embeddings = model.language_model.model.embed_tokens.weight.data
+ mu = torch.mean(pre_expansion_embeddings, dim=0).float()
+ n = pre_expansion_embeddings.size()[0]
+ sigma = ((pre_expansion_embeddings - mu).T @ (pre_expansion_embeddings - mu)) / n
+ dist = torch.distributions.multivariate_normal.MultivariateNormal(mu, covariance_matrix=1e-5 * sigma)
+
+ # We add an image token so we resize the model
+ # Pad to 64 for performance reasons
+ # Qwen-based models have extra unused space in the vocab size already, so no need to resize
+ if model_id not in ["lmms-lab/llava-next-72b", "lmms-lab/llava-next-110b"]:
+ pad_shape = 64
+ vocab_size = config.text_config.vocab_size
+ if model_id == "liuhaotian/llava-v1.6-34b":
+ # this one has 3 additional tokens, namely <|startoftext|>, <|endoftext|> and
+ num_tokens = vocab_size + 3
+ else:
+ # this one has 2 additional tokens, namely and
+ num_tokens = vocab_size + 2
+ model.resize_token_embeddings(num_tokens, pad_to_multiple_of=pad_shape)
+ model.language_model.model.embed_tokens.weight.data[vocab_size:] = torch.stack(
+ tuple(
+ dist.sample() for _ in range(model.language_model.model.embed_tokens.weight.data[vocab_size:].shape[0])
+ ),
+ dim=0,
+ )
+ model.language_model.lm_head.weight.data[vocab_size:] = torch.stack(
+ tuple(dist.sample() for _ in range(model.language_model.lm_head.weight.data[vocab_size:].shape[0])),
+ dim=0,
+ )
+
+ print(f"Saving model and processor for {model_id} to {pytorch_dump_folder_path}")
+ Path(pytorch_dump_folder_path).mkdir(exist_ok=True)
+ model.save_pretrained(pytorch_dump_folder_path)
+ processor.save_pretrained(pytorch_dump_folder_path)
+
+ # Make space so we can load the model properly now.
+ del state_dict
+ gc.collect()
+
+ # Load everything back for inference tests in float32 because prev script was written as that
+ # Though it's mostly loaded in fp16 as original weights are in fp16
+ model = LlavaNextForConditionalGeneration.from_pretrained(pytorch_dump_folder_path, device_map="auto")
+ processor = LlavaNextProcessor.from_pretrained(pytorch_dump_folder_path)
+ device = model.device
+
+ # prepare inputs
+ image = load_image()
+ if model_id == "liuhaotian/llava-v1.6-mistral-7b":
+ prompt = "[INST] \nWhat is shown in this image? [/INST]"
+ elif model_id in ["liuhaotian/llava-v1.6-vicuna-7b", "liuhaotian/llava-v1.6-vicuna-13b"]:
+ prompt = "A chat between a curious human and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the human's questions. USER: \nWhat is shown in this image? ASSISTANT:"
+ elif model_id == "liuhaotian/llava-v1.6-34b":
+ prompt = "<|im_start|>system\nAnswer the questions.<|im_end|><|im_start|>user\n\nWhat is shown in this image?<|im_end|><|im_start|>assistant\n"
+ elif model_id == "lmms-lab/llama3-llava-next-8b":
+ prompt = "<|start_header_id|>system<|end_header_id|>\n\nYou are a helpful language and vision assistant. You are able to understand the visual content that the user provides, and assist the user with a variety of tasks using natural language.<|eot_id|><|start_header_id|><|start_header_id|>user<|end_header_id|>\n\n\nWhat is shown in this image?<|eot_id|><|start_header_id|>assistant<|end_header_id|>\n\n"
+ elif model_id in ["lmms-lab/llava-next-72b", "lmms-lab/llava-next-110b"]:
+ prompt = "<|im_start|>system\nYou are a helpful assistant.<|im_end|>\n<|im_start|>user\n\nWhat is shown in this image?<|im_end|>\n<|im_start|>assistant\n"
+
+ inputs = processor(images=image, text=prompt, return_tensors="pt")
+
+ # verify inputs
+ filepath = hf_hub_download(repo_id="nielsr/test-image", filename="llava_1_6_pixel_values.pt", repo_type="dataset")
+ original_pixel_values = torch.load(filepath, map_location="cpu", weights_only=True)
+ assert torch.allclose(original_pixel_values, inputs.pixel_values.half())
+
+ if model_id == "liuhaotian/llava-v1.6-mistral-7b":
+ filepath = hf_hub_download(repo_id="nielsr/test-image", filename="llava_1_6_input_ids.pt", repo_type="dataset")
+ original_input_ids = torch.load(filepath, map_location="cpu", weights_only=True)
+ # replace -200 by image_token_id (since we use token ID = 32000 for the image token)
+ original_input_ids[original_input_ids == -200] = image_token_id
+ assert original_input_ids[0].tolist() == inputs.input_ids[0].tolist()
+
+ elif model_id == "liuhaotian/llava-v1.6-34b":
+ filepath = hf_hub_download(
+ repo_id="nielsr/test-image", filename="llava_1_6_34b_input_ids.pt", repo_type="dataset"
+ )
+ original_input_ids = torch.load(filepath, map_location="cpu", weights_only=True)
+ # replace -200 by image_token_id
+ original_input_ids[original_input_ids == -200] = image_token_id
+
+ assert original_input_ids[0].tolist() == inputs.input_ids[0].tolist()
+
+ image_sizes = torch.tensor([[899, 1024]])
+ assert image_sizes[0].tolist() == inputs.image_sizes[0].tolist()
+
+ # verify single forward pass
+ print("Single forward pass")
+ with torch.inference_mode():
+ inputs = inputs.to(device)
+ outputs = model(**inputs)
+ print("Shape of logits:", outputs.logits.shape)
+ print("First values of logits:", outputs.logits[0, :3, :3])
+
+ if model_id == "liuhaotian/llava-v1.6-mistral-7b":
+ expected_slice = torch.tensor(
+ [[-4.8555, -4.6992, -0.1996], [-10.5703, -10.7344, -2.7246], [-7.0391, -7.3672, -0.2634]],
+ dtype=torch.float32,
+ device=device,
+ )
+ elif model_id == "liuhaotian/llava-v1.6-vicuna-7b":
+ expected_slice = torch.tensor(
+ [[1.4883, 0.9976, -0.6992], [-9.7031, -5.7031, -1.5557], [-5.1328, -5.5586, 8.8281]],
+ dtype=torch.float32,
+ device=device,
+ )
+ elif model_id == "liuhaotian/llava-v1.6-vicuna-13b":
+ expected_slice = torch.tensor(
+ [[-0.9614, 7.3125, 0.2106], [-7.2695, -8.5469, 3.6211], [-6.3750, -8.1875, 5.4688]],
+ dtype=torch.float32,
+ device=device,
+ )
+ elif model_id == "liuhaotian/llava-v1.6-34b":
+ expected_slice = torch.tensor(
+ [[-9.0859, -9.1406, 5.9453], [-5.9570, -5.9766, 2.2754], [-5.7305, -5.7539, 4.0000]],
+ dtype=torch.float32,
+ device=device,
+ )
+ elif model_id == "lmms-lab/llama3-llava-next-8b":
+ expected_slice = torch.tensor(
+ [[-3.9648, 1.1396, 3.3145], [-5.3594, -1.5654, -1.9619], [-12.3750, -10.6797, -9.3125]],
+ dtype=torch.float32,
+ device=device,
+ )
+ elif model_id == "lmms-lab/llava-next-72b":
+ # Not yet checked against reference
+ expected_slice = torch.tensor(
+ [[3.7148, 3.9277, 3.4395], [-0.4341, 1.1387, 6.5117], [3.2324, 3.4688, 4.1133]],
+ dtype=torch.float32,
+ device=device,
+ )
+ elif model_id == "lmms-lab/llava-next-110b":
+ # Not yet checked against reference
+ expected_slice = torch.tensor(
+ [[-2.5449, -1.6738, -2.0371], [1.0811, 3.4961, 5.0312], [1.7803, 2.5137, 2.4277]],
+ dtype=torch.float32,
+ device=device,
+ )
+ else:
+ raise ValueError(f"Model {model_id} not supported")
+
+ assert torch.allclose(outputs.logits[0, :3, :3], expected_slice, atol=1e-4)
+ print("Logits are ok!")
+
+ # verify generation
+ output_ids = model.generate(
+ **inputs,
+ max_new_tokens=100,
+ use_cache=True,
+ )
+
+ generated_text = processor.batch_decode(output_ids, skip_special_tokens=True)[0].strip()
+
+ print("Generated text:", repr(generated_text))
+
+ if model_id == "liuhaotian/llava-v1.6-mistral-7b":
+ expected_text = '[INST] \nWhat is shown in this image? [/INST] The image appears to be a radar chart, which is a type of multi-dimensional plot that displays data in the form of a two-dimensional chart of three or more quantitative variables represented on axes starting from the same point.\n\nIn this particular radar chart, there are several axes labeled with different metrics or benchmarks, such as "MMM-Vet," "MMM-Bench," "LLaVA-Bench," "SLED-Bench," "'
+ elif model_id == "liuhaotian/llava-v1.6-vicuna-7b":
+ expected_text = """A chat between a curious human and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the human\'s questions. USER: \nWhat is shown in this image? ASSISTANT: The image appears to be a graphical representation of a benchmarking study comparing the performance of various models or systems. It\'s a scatter plot with a circular layout, where each point represents a different model or system, and the axes represent different metrics or dimensions of comparison.\n\nThe metrics are likely related to machine learning or artificial intelligence performance, as indicated by the terms like "BLIP-2," "Instruct BLIP," "POE," "QWA," "V"""
+ elif model_id == "liuhaotian/llava-v1.6-vicuna-13b":
+ expected_text = "A chat between a curious human and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the human's questions. USER: \nWhat is shown in this image? ASSISTANT: The image appears to be a radar chart, also known as a spider chart or star chart, which is a graphical method of displaying multivariate data in the form of a two-dimensional chart of three or more quantitative variables represented on axes starting from the same point.\n\nIn this particular radar chart, there are several variables represented:\n\n- MM-Vet\n- LLa-Va-Bench\n- SEED-Bench\n- MM"
+ elif model_id == "liuhaotian/llava-v1.6-34b":
+ expected_text = "<|im_start|> system\nAnswer the questions. <|im_start|> user\n\nWhat is shown in this image? <|im_start|> assistant\nThe image appears to be a radar chart, also known as a spider chart, which is a graphical method of displaying multivariate data in the form of a two-dimensional chart of three or more quantitative variables represented on axes starting from the same point.\n\nIn this particular chart, there are several datasets represented by different colors and labeled with various acronyms such as MM-Vet, LLaVA-Bench, SEED-Bench, MM-Bench-CN, MM-"
+ elif model_id == "lmms-lab/llama3-llava-next-8b":
+ expected_text = 'system\n\nYou are a helpful language and vision assistant. You are able to understand the visual content that the user provides, and assist the user with a variety of tasks using natural language.user\n\n\nWhat is shown in this image?assistant\n\n\nThe image shows a radar chart, also known as a spider chart or a web chart, which is a type of graph used to display multivariate data in the form of a two-dimensional chart of three or more quantitative variables represented on axes starting from the same point. Each axis represents a different variable, and the values are plotted along each axis and connected to form a polygon.\n\nIn this particular radar chart, there are several axes labeled with different variables, such as "MM-Vet," "LL'
+ elif model_id == "lmms-lab/llava-next-72b":
+ expected_text = "system\nYou are a helpful assistant.\nuser\n\nWhat is shown in this image?\nassistant\nThe image displays a radar chart, also known as a spider chart or a star chart, which is a graphical method of displaying multivariate data in the form of a two-dimensional chart of three or more quantitative variables represented on axes starting from the same point. Each axis represents a different variable, and the value of each variable is represented by the distance from the center of the chart to the point where the axis intersects with the line representing that variable's value.\n\nIn this particular chart, there are several axes"
+ elif model_id == "lmms-lab/llava-next-110b":
+ expected_text = "system\nYou are a helpful assistant.\nuser\n\nWhat is shown in this image?\nassistant\nThe image shows a radar chart comparing the performance of different models on various visual question answering (VQA) benchmarks. Each colored line represents a different model, and the distance from the center of the chart indicates the score or performance level of the model on a particular benchmark. The benchmarks are labeled around the edges of the chart, and include VQA v2, GQA, VizWiz, TextVQA, MMBench-CN, MME, and others. The chart allows for a"
+ else:
+ raise ValueError(f"Model {model_id} not supported")
+
+ assert generated_text == expected_text
+ print("Generated text is ok!")
+
+ # verify batched generation
+ print("Batched generation...")
+ url = "http://images.cocodataset.org/val2017/000000039769.jpg"
+ with httpx.stream("GET", url) as response:
+ cats_image = Image.open(BytesIO(response.read()))
+
+ inputs = processor(
+ images=[image, cats_image],
+ text=[prompt, prompt],
+ padding=True,
+ return_tensors="pt",
+ ).to(device)
+
+ for k, v in inputs.items():
+ print(k, v.shape)
+
+ print("Image sizes:", inputs.image_sizes)
+
+ # make sure image_sizes are the same
+ # as otherwise batched generation doesn't work
+ inputs.image_sizes[1] = inputs.image_sizes[0]
+
+ print("Batched generation...")
+ output_ids = model.generate(
+ **inputs,
+ max_new_tokens=20,
+ use_cache=True,
+ )
+
+ outputs = tokenizer.batch_decode(output_ids, skip_special_tokens=True)
+ print(outputs)
+
+ if push_to_hub:
+ checkpoint_name = model_id.split("/")[-1]
+ print(f"Pushing to repo llava-hf/{checkpoint_name}-hf")
+ model.push_to_hub(f"llava-hf/{checkpoint_name}-hf")
+ processor.push_to_hub(f"llava-hf/{checkpoint_name}-hf")
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "--model_id",
+ help="Hub location of the model to convert",
+ default="liuhaotian/llava-v1.6-mistral-7b",
+ choices=[
+ "liuhaotian/llava-v1.6-mistral-7b",
+ "liuhaotian/llava-v1.6-vicuna-7b",
+ "liuhaotian/llava-v1.6-vicuna-13b",
+ "liuhaotian/llava-v1.6-34b",
+ "lmms-lab/llama3-llava-next-8b",
+ "lmms-lab/llava-next-72b",
+ "lmms-lab/llava-next-110b",
+ ],
+ required=False,
+ )
+ parser.add_argument(
+ "--pytorch_dump_folder_path", type=str, required=True, help="Path to the output PyTorch model directory."
+ )
+ parser.add_argument(
+ "--push_to_hub",
+ action="store_true",
+ help="Whether or not to push the converted model to the Hugging Face hub.",
+ )
+ args = parser.parse_args()
+
+ convert_llava_to_hf(args.model_id, args.pytorch_dump_folder_path, args.push_to_hub)
diff --git a/third_party/transformers/src/transformers/models/llava_next/image_processing_llava_next.py b/third_party/transformers/src/transformers/models/llava_next/image_processing_llava_next.py
new file mode 100644
index 0000000000000000000000000000000000000000..0b5c3b0c18afef59cde3d7dd12a2f0a93504c99c
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/llava_next/image_processing_llava_next.py
@@ -0,0 +1,243 @@
+# Copyright 2024 The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Image processor class for LLaVa-NeXT."""
+
+from typing import Union
+
+import torch
+from torchvision.transforms.v2 import functional as tvF
+
+from ...image_processing_backends import TorchvisionBackend
+from ...image_processing_utils import (
+ BatchFeature,
+ get_patch_output_size,
+ select_best_resolution,
+)
+from ...image_transforms import divide_to_patches, group_images_by_shape, reorder_images
+from ...image_utils import (
+ OPENAI_CLIP_MEAN,
+ OPENAI_CLIP_STD,
+ ChannelDimension,
+ ImageInput,
+ PILImageResampling,
+ SizeDict,
+ get_image_size,
+)
+from ...processing_utils import ImagesKwargs, Unpack
+from ...utils import TensorType, auto_docstring
+
+
+class LlavaNextImageProcessorKwargs(ImagesKwargs, total=False):
+ r"""
+ image_grid_pinpoints (`list[list[int]]`, *optional*):
+ A list of possible resolutions to use for processing high resolution images. The best resolution is selected
+ based on the original size of the image. Can be overridden by `image_grid_pinpoints` in the `preprocess`
+ method.
+ """
+
+ image_grid_pinpoints: list[list[int]]
+
+
+@auto_docstring
+class LlavaNextImageProcessor(TorchvisionBackend):
+ model_input_names = ["pixel_values", "image_sizes"]
+ valid_kwargs = LlavaNextImageProcessorKwargs
+
+ resample = PILImageResampling.BICUBIC
+ image_mean = OPENAI_CLIP_MEAN
+ image_std = OPENAI_CLIP_STD
+ size = {"shortest_edge": 224}
+ default_to_square = False
+ crop_size = {"height": 224, "width": 224}
+ do_resize = True
+ do_center_crop = True
+ do_rescale = True
+ do_normalize = True
+ do_convert_rgb = True
+ do_pad = True
+ image_grid_pinpoints = [[336, 672], [672, 336], [672, 672], [1008, 336], [336, 1008]]
+
+ def __init__(self, **kwargs: Unpack[LlavaNextImageProcessorKwargs]):
+ super().__init__(**kwargs)
+
+ @auto_docstring
+ def preprocess(
+ self, images: ImageInput | list[ImageInput], *args, **kwargs: Unpack[LlavaNextImageProcessorKwargs]
+ ) -> BatchFeature:
+ return super().preprocess(images, *args, **kwargs)
+
+ def _get_padding_size(self, original_resolution: tuple, target_resolution: tuple):
+ """Get padding size for patching (returns list format for tvF.pad)."""
+ original_height, original_width = original_resolution
+ target_height, target_width = target_resolution
+ paste_x, r_x = divmod(target_width - original_width, 2)
+ paste_y, r_y = divmod(target_height - original_height, 2)
+ return [paste_x, paste_y, paste_x + r_x, paste_y + r_y]
+
+ def _resize_for_patching(
+ self,
+ image: "torch.Tensor",
+ target_resolution: tuple,
+ resample: Union["PILImageResampling", "tvF.InterpolationMode", int] | None,
+ input_data_format: ChannelDimension,
+ ) -> "torch.Tensor":
+ """Resizes an image to a target resolution while maintaining aspect ratio."""
+ new_height, new_width = get_patch_output_size(image, target_resolution, input_data_format)
+ resized_image = self.resize(
+ image=image,
+ size=SizeDict(height=new_height, width=new_width),
+ resample=resample,
+ )
+
+ return resized_image
+
+ def _pad_for_patching(self, image: "torch.Tensor", target_resolution: tuple) -> "torch.Tensor":
+ """Pad an image to a target resolution while maintaining aspect ratio."""
+ new_resolution = get_patch_output_size(image, target_resolution, input_data_format=ChannelDimension.FIRST)
+ padding = self._get_padding_size(new_resolution, target_resolution)
+
+ padded_image = tvF.pad(image, padding=padding)
+
+ return padded_image
+
+ def _get_image_patches(
+ self,
+ image: "torch.Tensor",
+ grid_pinpoints: list[list[int]],
+ size: tuple,
+ patch_size: int,
+ resample: Union["PILImageResampling", "tvF.InterpolationMode", int] | None,
+ ) -> list["torch.Tensor"]:
+ """Process an image with variable resolutions by dividing it into patches."""
+ if not isinstance(grid_pinpoints, list):
+ raise TypeError("grid_pinpoints must be a list of possible resolutions.")
+
+ possible_resolutions = grid_pinpoints
+
+ image_size = get_image_size(image, channel_dim=ChannelDimension.FIRST)
+ best_resolution = select_best_resolution(image_size, possible_resolutions)
+ resized_image = self._resize_for_patching(
+ image, best_resolution, resample=resample, input_data_format=ChannelDimension.FIRST
+ )
+ padded_image = self._pad_for_patching(resized_image, best_resolution)
+ patches = divide_to_patches(padded_image, patch_size=patch_size)
+ # Resize original image using backend's resize method (handles resample conversion)
+ # size is a tuple (height, width), convert to SizeDict
+ size_height, size_width = size
+ resized_original_image = self.resize(
+ image=image,
+ size=SizeDict(height=size_height, width=size_width),
+ resample=resample,
+ )
+
+ image_patches = [resized_original_image] + patches
+
+ return image_patches
+
+ def _pad_for_batching(
+ self,
+ pixel_values: list["torch.Tensor"],
+ ) -> list["torch.Tensor"]:
+ """Pads images on the `num_of_patches` dimension with zeros to form a batch of same number of patches."""
+ max_patch = max(len(x) for x in pixel_values)
+ pixel_values = [
+ torch.nn.functional.pad(image, pad=[0, 0, 0, 0, 0, 0, 0, max_patch - image.shape[0]])
+ for image in pixel_values
+ ]
+
+ return pixel_values
+
+ def _preprocess(
+ self,
+ images: list["torch.Tensor"],
+ do_resize: bool,
+ size: SizeDict,
+ image_grid_pinpoints: list[list[int]],
+ resample: "PILImageResampling | tvF.InterpolationMode | int | None",
+ do_center_crop: bool,
+ crop_size: SizeDict,
+ do_rescale: bool,
+ rescale_factor: float,
+ do_normalize: bool,
+ image_mean: float | list[float] | None,
+ image_std: float | list[float] | None,
+ do_pad: bool | None,
+ disable_grouping: bool | None,
+ return_tensors: str | TensorType | None,
+ **kwargs,
+ ) -> BatchFeature:
+ """Custom preprocessing for LLaVA-NeXT with patch processing."""
+ processed_images = []
+ image_sizes = []
+
+ # Backend's resize method handles resample conversion, so we can pass it directly
+ # Determine the size tuple
+ if size and size.height and size.width:
+ size_tuple = (size.height, size.width)
+ else:
+ size_tuple = (size.shortest_edge, size.shortest_edge)
+
+ # Determine the patch size
+ if crop_size and crop_size.height:
+ patch_size = crop_size.height
+ elif size and size.height:
+ patch_size = size.height
+ else:
+ patch_size = size.shortest_edge
+
+ for image in images:
+ image_patches = self._get_image_patches(
+ image,
+ image_grid_pinpoints,
+ size=size_tuple,
+ patch_size=patch_size,
+ resample=resample,
+ )
+
+ # Group images by size for batched processing
+ processed_image_patches_grouped = {}
+ grouped_image_patches, grouped_image_patches_index = group_images_by_shape(
+ image_patches, disable_grouping=disable_grouping
+ )
+ for shape, stacked_image_patches in grouped_image_patches.items():
+ if do_resize:
+ stacked_image_patches = self.resize(
+ image=stacked_image_patches,
+ size=size,
+ resample=resample,
+ )
+ if do_center_crop:
+ stacked_image_patches = self.center_crop(stacked_image_patches, crop_size)
+ # Fused rescale and normalize
+ # Convert lists to tuples for lru_cache compatibility
+ image_mean_tuple = tuple(image_mean) if isinstance(image_mean, list) else image_mean
+ image_std_tuple = tuple(image_std) if isinstance(image_std, list) else image_std
+ stacked_image_patches = self.rescale_and_normalize(
+ stacked_image_patches, do_rescale, rescale_factor, do_normalize, image_mean_tuple, image_std_tuple
+ )
+ processed_image_patches_grouped[shape] = stacked_image_patches
+ processed_image_patches = reorder_images(processed_image_patches_grouped, grouped_image_patches_index)
+ processed_image_patches = torch.stack(processed_image_patches, dim=0)
+ processed_images.append(processed_image_patches)
+ image_sizes.append(get_image_size(image, ChannelDimension.FIRST))
+
+ if do_pad:
+ processed_images = self._pad_for_batching(processed_images)
+
+ return BatchFeature(
+ data={"pixel_values": processed_images, "image_sizes": image_sizes}, tensor_type=return_tensors
+ )
+
+
+__all__ = ["LlavaNextImageProcessor"]
diff --git a/third_party/transformers/src/transformers/models/llava_next/image_processing_pil_llava_next.py b/third_party/transformers/src/transformers/models/llava_next/image_processing_pil_llava_next.py
new file mode 100644
index 0000000000000000000000000000000000000000..8e742529c2d0f35e5a596c85e092dc0a1cb888d0
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/llava_next/image_processing_pil_llava_next.py
@@ -0,0 +1,239 @@
+# Copyright 2024 The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Image processor class for LLaVa-NeXT."""
+
+import numpy as np
+
+from ...image_processing_backends import PilBackend
+from ...image_processing_utils import (
+ BatchFeature,
+ get_patch_output_size,
+ select_best_resolution,
+)
+from ...image_transforms import divide_to_patches
+from ...image_utils import (
+ OPENAI_CLIP_MEAN,
+ OPENAI_CLIP_STD,
+ ChannelDimension,
+ ImageInput,
+ PILImageResampling,
+ SizeDict,
+)
+from ...processing_utils import ImagesKwargs, Unpack
+from ...utils import TensorType, auto_docstring
+
+
+# Adapted from transformers.models.llava_next.image_processing_llava_next.LlavaNextImageProcessorKwargs
+class LlavaNextImageProcessorKwargs(ImagesKwargs, total=False):
+ r"""
+ image_grid_pinpoints (`list[list[int]]`, *optional*):
+ A list of possible resolutions to use for processing high resolution images. The best resolution is selected
+ based on the original size of the image. Can be overridden by `image_grid_pinpoints` in the `preprocess`
+ method.
+ """
+
+ image_grid_pinpoints: list[list[int]]
+
+
+@auto_docstring
+class LlavaNextImageProcessorPil(PilBackend):
+ model_input_names = ["pixel_values", "image_sizes"]
+ valid_kwargs = LlavaNextImageProcessorKwargs
+
+ resample = PILImageResampling.BICUBIC
+ image_mean = OPENAI_CLIP_MEAN
+ image_std = OPENAI_CLIP_STD
+ size = {"shortest_edge": 224}
+ default_to_square = False
+ crop_size = {"height": 224, "width": 224}
+ do_resize = True
+ do_center_crop = True
+ do_rescale = True
+ do_normalize = True
+ do_convert_rgb = True
+ do_pad = True
+ image_grid_pinpoints = [[336, 672], [672, 336], [672, 672], [1008, 336], [336, 1008]]
+
+ def __init__(self, **kwargs: Unpack[LlavaNextImageProcessorKwargs]):
+ super().__init__(**kwargs)
+
+ @auto_docstring
+ def preprocess(
+ self, images: ImageInput | list[ImageInput], *args, **kwargs: Unpack[LlavaNextImageProcessorKwargs]
+ ) -> BatchFeature:
+ return super().preprocess(images, *args, **kwargs)
+
+ def _get_padding_size(self, original_resolution: tuple, target_resolution: tuple):
+ """Get padding size for patching (returns tuple format for np.pad)."""
+ original_height, original_width = original_resolution
+ target_height, target_width = target_resolution
+ paste_x, r_x = divmod(target_width - original_width, 2)
+ paste_y, r_y = divmod(target_height - original_height, 2)
+ return (paste_y, paste_y + r_y), (paste_x, paste_x + r_x)
+
+ def _resize_for_patching(
+ self,
+ image: np.ndarray,
+ target_resolution: tuple,
+ resample: PILImageResampling,
+ ) -> np.ndarray:
+ """Resizes an image to a target resolution while maintaining aspect ratio."""
+ new_height, new_width = get_patch_output_size(
+ image, target_resolution, input_data_format=ChannelDimension.FIRST
+ )
+ resized_image = self.resize(image=image, size=SizeDict(height=new_height, width=new_width), resample=resample)
+
+ return resized_image
+
+ def _pad_for_patching(self, image: np.ndarray, target_resolution: tuple) -> np.ndarray:
+ """Pad an image to a target resolution while maintaining aspect ratio."""
+ new_resolution = get_patch_output_size(image, target_resolution, input_data_format=ChannelDimension.FIRST)
+ padding_hw = self._get_padding_size(new_resolution, target_resolution)
+
+ # For channels_first format (C, H, W), add (0, 0) for channel dimension
+ # padding_hw is ((before_h, after_h), (before_w, after_w))
+ # np.pad expects ((before_C, after_C), (before_H, after_H), (before_W, after_W))
+ padding = ((0, 0), padding_hw[0], padding_hw[1])
+
+ # Use np.pad directly for patching padding
+ padded_image = np.pad(image, padding, mode="constant", constant_values=0)
+
+ return padded_image
+
+ def get_image_patches(
+ self,
+ image: np.ndarray,
+ grid_pinpoints: list[list[int]],
+ size: tuple,
+ patch_size: int,
+ resample: PILImageResampling,
+ ) -> list[np.ndarray]:
+ """Process an image with variable resolutions by dividing it into patches."""
+ if not isinstance(grid_pinpoints, list):
+ raise TypeError("grid_pinpoints must be a list of possible resolutions.")
+
+ possible_resolutions = grid_pinpoints
+
+ image_size = image.shape[-2:]
+ best_resolution = select_best_resolution(image_size, possible_resolutions)
+ resized_image = self._resize_for_patching(image, best_resolution, resample=resample)
+ padded_image = self._pad_for_patching(resized_image, best_resolution)
+
+ patches = divide_to_patches(padded_image, patch_size=patch_size)
+
+ size_height, size_width = size
+ resized_original_image = self.resize(
+ image=image,
+ size=SizeDict(height=size_height, width=size_width),
+ resample=resample,
+ )
+
+ image_patches = [resized_original_image] + patches
+
+ return image_patches
+
+ def _pad_for_batching(
+ self,
+ pixel_values: list[np.ndarray],
+ ) -> list[np.ndarray]:
+ """Pads images on the `num_of_patches` dimension with zeros to form a batch of same number of patches."""
+ max_patch = max(len(x) for x in pixel_values)
+ # Use np.pad directly for patch dimension padding
+ padded_values = []
+ for image in pixel_values:
+ # Padding format: ((before_dim0, after_dim0), (before_dim1, after_dim1), ...)
+ padding = ((0, max_patch - image.shape[0]), (0, 0), (0, 0), (0, 0))
+ padded_image = np.pad(image, padding, mode="constant", constant_values=0)
+ padded_values.append(padded_image)
+
+ return padded_values
+
+ def _preprocess(
+ self,
+ images: list[np.ndarray],
+ do_resize: bool,
+ size: SizeDict,
+ image_grid_pinpoints: list[list[int]],
+ resample: "PILImageResampling | None",
+ do_center_crop: bool,
+ crop_size: SizeDict,
+ do_rescale: bool,
+ rescale_factor: float,
+ do_normalize: bool,
+ image_mean: float | list[float] | None,
+ image_std: float | list[float] | None,
+ do_pad: bool | None,
+ return_tensors: str | TensorType | None,
+ **kwargs,
+ ) -> BatchFeature:
+ """Custom preprocessing for LLaVA-NeXT with patch processing."""
+ processed_images = []
+ image_sizes = []
+
+ # Backend's resize method handles resample conversion, so we can pass it directly
+ # Determine the size tuple
+ if size and size.height and size.width:
+ size_tuple = (size.height, size.width)
+ else:
+ size_tuple = (size.shortest_edge, size.shortest_edge)
+
+ # Determine the patch size
+ if crop_size and crop_size.height:
+ patch_size = crop_size.height
+ elif size and size.height:
+ patch_size = size.height
+ else:
+ patch_size = size.shortest_edge
+
+ for image in images:
+ # convert image into a list of patches
+ # we intentionally use the same data format as the input data format
+ image_patches = self.get_image_patches(
+ image,
+ image_grid_pinpoints,
+ size=size_tuple,
+ patch_size=patch_size,
+ resample=resample,
+ )
+
+ # preprocess patches
+ pixel_values = []
+ for patch in image_patches:
+ if do_resize:
+ patch = self.resize(image=patch, size=size, resample=resample)
+
+ if do_center_crop:
+ patch = self.center_crop(image=patch, size=crop_size)
+
+ if do_rescale:
+ patch = self.rescale(image=patch, scale=rescale_factor)
+
+ if do_normalize:
+ patch = self.normalize(image=patch, mean=image_mean, std=image_std)
+
+ pixel_values.append(patch)
+
+ pixel_values = np.array(pixel_values)
+ processed_images.append(pixel_values)
+ image_sizes.append(image.shape[-2:])
+
+ if do_pad:
+ processed_images = self._pad_for_batching(processed_images)
+
+ return BatchFeature(
+ data={"pixel_values": processed_images, "image_sizes": image_sizes}, tensor_type=return_tensors
+ )
+
+
+__all__ = ["LlavaNextImageProcessorPil"]
diff --git a/third_party/transformers/src/transformers/models/llava_next/modeling_llava_next.py b/third_party/transformers/src/transformers/models/llava_next/modeling_llava_next.py
new file mode 100644
index 0000000000000000000000000000000000000000..2443669f109b43d3c89c4bc7c59ef5ec5b29f38b
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/llava_next/modeling_llava_next.py
@@ -0,0 +1,689 @@
+# Copyright 2024 the HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""PyTorch Llava-NeXT model."""
+
+import math
+from dataclasses import dataclass
+
+import numpy as np
+import torch
+from torch import nn
+
+from ... import initialization as init
+from ...activations import ACT2FN
+from ...cache_utils import Cache
+from ...generation import GenerationMixin
+from ...image_processing_utils import select_best_resolution
+from ...modeling_flash_attention_utils import FlashAttentionKwargs
+from ...modeling_outputs import BaseModelOutputWithPast, BaseModelOutputWithPooling, ModelOutput
+from ...modeling_utils import PreTrainedModel
+from ...processing_utils import Unpack
+from ...utils import TransformersKwargs, auto_docstring, logging, torch_compilable_check
+from ...utils.generic import can_return_tuple, merge_with_config_defaults
+from ..auto import AutoModel
+from .configuration_llava_next import LlavaNextConfig
+
+
+logger = logging.get_logger(__name__)
+
+
+def get_anyres_image_grid_shape(image_size, grid_pinpoints, patch_size):
+ """
+ Calculate the shape of the image patch grid after the preprocessing for images of any resolution.
+
+ Args:
+ image_size (`tuple`):
+ The size of the input image in the format (width, height).
+ grid_pinpoints (`List`):
+ A list containing possible resolutions. Each item in the list should be a tuple or list
+ of the form `(height, width)`.
+ patch_size (`int`):
+ The size of each image patch.
+
+ Returns:
+ tuple: The shape of the image patch grid in the format (width, height).
+ """
+ if not isinstance(grid_pinpoints, list):
+ raise TypeError("grid_pinpoints should be a list of tuples or lists")
+
+ # ! VERY IMPORTANT if image_size is tensor, must convert to into tuple, otherwise it will cause wrong calculate
+ if not isinstance(image_size, (list, tuple)):
+ if not isinstance(image_size, (torch.Tensor, np.ndarray)):
+ raise TypeError(
+ f"image_size invalid type: {type(image_size)} not valid, should be either list, tuple, np.ndarray or tensor"
+ )
+ image_size = image_size.tolist()
+
+ height, width = select_best_resolution(image_size, grid_pinpoints)
+ return height // patch_size, width // patch_size
+
+
+def image_size_to_num_patches(image_size, grid_pinpoints, patch_size: int):
+ """
+ Calculate the number of patches after the preprocessing for images of any resolution.
+
+ Args:
+ image_size (`torch.LongTensor` or `np.ndarray` or `tuple[int, int]`):
+ The size of the input image in the format (height, width). ?
+ grid_pinpoints (`List`):
+ A list containing possible resolutions. Each item in the list should be a tuple or list
+ of the form `(height, width)`.
+ patch_size (`int`):
+ The size of each image patch.
+
+ Returns:
+ int: the number of patches
+ """
+ if not isinstance(grid_pinpoints, list):
+ raise TypeError("grid_pinpoints should be a list of tuples or lists")
+
+ # ! VERY IMPORTANT if image_size is tensor, must convert to into tuple, otherwise it will cause wrong calculate
+ if not isinstance(image_size, (list, tuple)):
+ if not isinstance(image_size, (torch.Tensor, np.ndarray)):
+ raise TypeError(f"image_size invalid type {type(image_size)} with value {image_size}")
+ image_size = image_size.tolist()
+
+ best_resolution = select_best_resolution(image_size, grid_pinpoints)
+ height, width = best_resolution
+ num_patches = 0
+ # consider change to ceil(height/patch_size)*ceil(width/patch_size) + 1
+ for i in range(0, height, patch_size):
+ for j in range(0, width, patch_size):
+ num_patches += 1
+ # add the base patch
+ num_patches += 1
+ return num_patches
+
+
+def unpad_image(tensor, original_size):
+ """
+ Unpads a PyTorch tensor of a padded and resized image.
+
+ Args:
+ tensor (`torch.Tensor`):
+ The image tensor, assumed to be of shape (num_channels, height, width).
+ original_size (`tuple`):
+ The original size of the image (height, width).
+
+ Returns:
+ `torch.Tensor`: The unpadded image tensor.
+ """
+ if not isinstance(original_size, (list, tuple)):
+ if not isinstance(original_size, (torch.Tensor, np.ndarray)):
+ raise TypeError(
+ f"image_size invalid type: {type(original_size)} not valid, should be either list, tuple, np.ndarray or tensor"
+ )
+ original_size = original_size.tolist()
+ original_height, original_width = original_size
+ current_height, current_width = tensor.shape[1:]
+
+ original_aspect_ratio = original_width / original_height
+ current_aspect_ratio = current_width / current_height
+
+ if original_aspect_ratio > current_aspect_ratio:
+ scale_factor = current_width / original_width
+ new_height = int(round(original_height * scale_factor, 7))
+ padding = (current_height - new_height) // 2
+ unpadded_tensor = tensor[:, padding : current_height - padding, :]
+ else:
+ scale_factor = current_height / original_height
+ new_width = int(round(original_width * scale_factor, 7))
+ padding = (current_width - new_width) // 2
+ unpadded_tensor = tensor[:, :, padding : current_width - padding]
+
+ return unpadded_tensor
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ Base class for Llava outputs, with hidden states and attentions.
+ """
+)
+class LlavaNextModelOutputWithPast(BaseModelOutputWithPast):
+ r"""
+ past_key_values (`Cache`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
+ It is a [`~cache_utils.Cache`] instance. For more details, see our [kv cache guide](https://huggingface.co/docs/transformers/en/kv_cache).
+
+ Contains pre-computed hidden-states (key and values in the self-attention blocks) that can be used (see
+ `past_key_values` input) to speed up sequential decoding.
+ image_hidden_states (`torch.FloatTensor`, *optional*):
+ A `torch.FloatTensor` of size `(batch_size, num_images, sequence_length, hidden_size)`.
+ image_hidden_states of the model produced by the vision encoder and after projecting the last hidden state.
+ """
+
+ image_hidden_states: torch.FloatTensor | None = None
+
+
+@dataclass
+@auto_docstring(
+ custom_intro="""
+ Base class for LlavaNext causal language model (or autoregressive) outputs.
+ """
+)
+class LlavaNextCausalLMOutputWithPast(ModelOutput):
+ r"""
+ loss (`torch.FloatTensor` of shape `(1,)`, *optional*, returned when `labels` is provided):
+ Language modeling loss (for next-token prediction).
+ logits (`torch.FloatTensor` of shape `(batch_size, sequence_length, config.vocab_size)`):
+ Prediction scores of the language modeling head (scores for each vocabulary token before SoftMax).
+ past_key_values (`Cache`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
+ It is a [`~cache_utils.Cache`] instance. For more details, see our [kv cache guide](https://huggingface.co/docs/transformers/en/kv_cache).
+
+ Contains pre-computed hidden-states (key and values in the self-attention blocks) that can be used (see
+ `past_key_values` input) to speed up sequential decoding.
+ image_hidden_states (`torch.FloatTensor`, *optional*):
+ A `torch.FloatTensor` of size (batch_size * num_patches, num_images, sequence_length, hidden_size)`.
+ image_hidden_states of the model produced by the vision encoder and after projecting the last hidden state.
+ """
+
+ loss: torch.FloatTensor | None = None
+ logits: torch.FloatTensor | None = None
+ past_key_values: Cache | None = None
+ hidden_states: tuple[torch.FloatTensor] | None = None
+ attentions: tuple[torch.FloatTensor] | None = None
+ image_hidden_states: torch.FloatTensor | None = None
+
+
+# Copied from transformers.models.llava.modeling_llava.LlavaMultiModalProjector with Llava->LlavaNext
+class LlavaNextMultiModalProjector(nn.Module):
+ def __init__(self, config: LlavaNextConfig):
+ super().__init__()
+ # We have hidden_size * the number of vision feature layers
+ num_feature_layers = 1 if isinstance(config.vision_feature_layer, int) else len(config.vision_feature_layer)
+ self.linear_1 = nn.Linear(
+ config.vision_config.hidden_size * num_feature_layers,
+ config.text_config.hidden_size,
+ bias=config.multimodal_projector_bias,
+ )
+ self.act = ACT2FN[config.projector_hidden_act]
+ self.linear_2 = nn.Linear(
+ config.text_config.hidden_size, config.text_config.hidden_size, bias=config.multimodal_projector_bias
+ )
+
+ def forward(self, image_features):
+ hidden_states = self.linear_1(image_features)
+ hidden_states = self.act(hidden_states)
+ hidden_states = self.linear_2(hidden_states)
+ return hidden_states
+
+
+@auto_docstring
+class LlavaNextPreTrainedModel(PreTrainedModel):
+ config: LlavaNextConfig
+ base_model_prefix = "model"
+ input_modalities = ("image", "text")
+ supports_gradient_checkpointing = True
+ _no_split_modules = ["LlamaDecoderLayer"]
+ _skip_keys_device_placement = "past_key_values"
+
+ _supports_flash_attn = True
+ _supports_sdpa = True
+
+ _can_compile_fullgraph = True
+ _supports_flex_attn = True
+ _supports_attention_backend = True
+
+ @torch.no_grad()
+ def _init_weights(self, module):
+ std = getattr(self.config, "initializer_range", self.config.get_text_config().initializer_range)
+
+ if isinstance(module, nn.Linear):
+ init.normal_(module.weight, mean=0.0, std=std)
+ if module.bias is not None:
+ init.zeros_(module.bias)
+ elif isinstance(module, LlavaNextModel):
+ embed_std = 1 / math.sqrt(self.config.text_config.hidden_size)
+ init.normal_(module.image_newline, mean=0.0, std=embed_std)
+
+
+@auto_docstring(
+ custom_intro="""
+ The Llava-Next model which consists of a vision backbone and a language model without language modeling head.
+ """
+)
+class LlavaNextModel(LlavaNextPreTrainedModel):
+ base_model_prefix = "model"
+
+ def __init__(self, config: LlavaNextConfig):
+ super().__init__(config)
+ self.vision_tower = AutoModel.from_config(config.vision_config)
+
+ self.multi_modal_projector = LlavaNextMultiModalProjector(config)
+ embed_std = 1 / math.sqrt(config.text_config.hidden_size)
+ self.image_newline = nn.Parameter(torch.randn(config.text_config.hidden_size, dtype=self.dtype) * embed_std)
+
+ self.vocab_size = config.text_config.vocab_size
+ self.language_model = AutoModel.from_config(config.text_config)
+ self.post_init()
+
+ def get_input_embeddings(self):
+ return self.language_model.get_input_embeddings()
+
+ def set_input_embeddings(self, value):
+ self.language_model.set_input_embeddings(value)
+
+ def pack_image_features(self, image_features, image_sizes, vision_feature_select_strategy, image_newline=None):
+ """
+ Reshape, unpad and then pack each image_feature into a single image_features tensor containing all visual vectors.
+
+ Args:
+ image_features (`list[torch.Tensor]` of length num_images, each of shape `(num_patches, image_length, embed_dim)`)
+ List of image feature tensor, each contains all the visual feature of all patches.
+ image_sizes (`torch.Tensor` of shape `(num_images, 2)`)
+ Actual image size of each images (H, W).
+ vision_feature_select_strategy (`str`)
+ The feature selection strategy used to select the vision feature from the vision backbone.
+ image_newline (`torch.Tensor` of shape `(embed_dim)`)
+ New line embedding vector.
+ Returns:
+ image_features (`torch.Tensor` of shape `(all_feat_len, embed_dim)`)
+ feature_lens (`list[int]`)
+ token length of each image in image_features
+ """
+ new_image_features = []
+ feature_lens = []
+ for image_idx, image_feature in enumerate(image_features):
+ if image_feature.shape[0] > 1:
+ base_image_feature = image_feature[0]
+ image_feature = image_feature[1:]
+ height = width = self.config.vision_config.image_size // self.config.vision_config.patch_size
+
+ num_patch_height, num_patch_width = get_anyres_image_grid_shape(
+ image_sizes[image_idx],
+ self.config.image_grid_pinpoints,
+ self.config.vision_config.image_size,
+ )
+
+ if (
+ np.prod(image_feature.shape) % (num_patch_height * num_patch_width * height * width) != 0
+ and vision_feature_select_strategy == "default"
+ ):
+ logger.warning_once(
+ "Image feature shape does not line up with the provided patch size. "
+ "You may be using the `default` vision_feature_select_strategy with a"
+ " visual encoder that does not have CLS."
+ )
+
+ image_feature = image_feature.view(num_patch_height, num_patch_width, height, width, -1)
+ image_feature = image_feature.permute(4, 0, 2, 1, 3).contiguous()
+ image_feature = image_feature.flatten(1, 2).flatten(2, 3)
+ image_feature = unpad_image(image_feature, image_sizes[image_idx])
+ if image_newline is not None:
+ image_feature = torch.cat(
+ (
+ image_feature,
+ image_newline[:, None, None]
+ .expand(*image_feature.shape[:-1], 1)
+ .to(image_feature.device, image_feature.dtype),
+ ),
+ dim=-1,
+ )
+ image_feature = image_feature.flatten(1, 2).transpose(0, 1)
+ image_feature = torch.cat((base_image_feature, image_feature), dim=0)
+ else:
+ image_feature = image_feature[0]
+ if image_newline is not None:
+ image_feature = torch.cat((image_feature, image_newline[None].to(image_feature)), dim=0)
+ new_image_features.append(image_feature)
+ feature_lens.append(image_feature.size(0))
+ feature_lens = torch.tensor(feature_lens, dtype=torch.long, device=image_features[0].device)
+ return new_image_features, feature_lens
+
+ @merge_with_config_defaults
+ @can_return_tuple
+ @auto_docstring(
+ custom_intro="Obtains image last hidden states from the vision tower and apply multimodal projection."
+ )
+ def get_image_features(
+ self,
+ pixel_values: torch.FloatTensor,
+ image_sizes: torch.Tensor,
+ vision_feature_layer: int | list[int] | list[int] | None = None,
+ vision_feature_select_strategy: str | None = None,
+ output_hidden_states: bool | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | BaseModelOutputWithPooling:
+ r"""
+ pixel_values (`torch.FloatTensor]` of shape `(batch_size, num_patches, channels, height, width)`)
+ The tensors corresponding to the input images.
+ image_sizes (`torch.Tensor` of shape `(num_images, 2)`)
+ Actual image size of each images (H, W).
+ vision_feature_layer (`Union[int, list[int]]`, *optional*):
+ The index of the layer to select the vision feature. If multiple indices are provided,
+ the vision feature of the corresponding indices will be concatenated to form the
+ vision features.
+ vision_feature_select_strategy (`str`, *optional*):
+ The feature selection strategy used to select the vision feature from the vision backbone.
+ Can be one of `"default"` or `"full"`
+ """
+ # ! infer image_num_patches from image_sizes
+ image_num_patches = [
+ image_size_to_num_patches(
+ image_size=imsize,
+ grid_pinpoints=self.config.image_grid_pinpoints,
+ patch_size=self.config.vision_config.image_size,
+ )
+ for imsize in image_sizes
+ ]
+ if pixel_values.dim() == 5:
+ # stacked if input is (batch_size, num_patches, num_channels, height, width)
+ _pixel_values_list = [pix_val[:num_patch] for pix_val, num_patch in zip(pixel_values, image_num_patches)]
+ pixel_values = torch.cat(_pixel_values_list, dim=0)
+ elif pixel_values.dim() != 4:
+ # otherwise has to be stacked from list of (num_patches, num_channels, height, width)
+ raise ValueError(f"pixel_values of shape {pixel_values.shape}, expect to be of 4 or 5 dimensions")
+
+ image_outputs = self.vision_tower(
+ pixel_values,
+ output_hidden_states=True, # Ignore arg on purpose
+ return_dict=True,
+ **kwargs,
+ )
+ # If we have one vision feature layer, return the corresponding hidden states,
+ # otherwise, select the hidden states of each feature layer and concatenate them
+ if isinstance(vision_feature_layer, int):
+ selected_image_feature = image_outputs.hidden_states[vision_feature_layer]
+ else:
+ hs_pool = [image_outputs.hidden_states[layer_idx] for layer_idx in vision_feature_layer]
+ selected_image_feature = torch.cat(hs_pool, dim=-1)
+
+ if vision_feature_select_strategy == "default":
+ selected_image_feature = selected_image_feature[:, 1:]
+
+ image_features = self.multi_modal_projector(selected_image_feature)
+ image_features = torch.split(image_features, image_num_patches, dim=0)
+
+ # NOTE we only support multimodal_patch_merge_type == "spatial_unpad"
+ image_features, feature_lens = self.pack_image_features(
+ image_features,
+ image_sizes,
+ vision_feature_select_strategy=vision_feature_select_strategy,
+ image_newline=self.image_newline,
+ )
+ image_outputs.pooler_output = image_features
+
+ return image_outputs
+
+ def get_placeholder_mask(
+ self, input_ids: torch.LongTensor, inputs_embeds: torch.FloatTensor, image_features: torch.FloatTensor
+ ):
+ """
+ Obtains multimodal placeholder mask from `input_ids` or `inputs_embeds`, and checks that the placeholder token count is
+ equal to the length of multimodal features. If the lengths are different, an error is raised.
+ """
+ if input_ids is None:
+ special_image_mask = inputs_embeds == self.get_input_embeddings()(
+ torch.tensor(self.config.image_token_id, dtype=torch.long, device=inputs_embeds.device)
+ )
+ special_image_mask = special_image_mask.all(-1)
+ else:
+ special_image_mask = input_ids == self.config.image_token_id
+
+ n_image_tokens = special_image_mask.sum()
+ special_image_mask = special_image_mask.unsqueeze(-1).expand_as(inputs_embeds).to(inputs_embeds.device)
+ torch_compilable_check(
+ inputs_embeds[special_image_mask].numel() == image_features.numel(),
+ f"Image features and image tokens do not match, tokens: {n_image_tokens}, features: {image_features.shape[0]}",
+ )
+ return special_image_mask
+
+ @merge_with_config_defaults
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ pixel_values: torch.FloatTensor | None = None,
+ image_sizes: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ vision_feature_layer: int | list[int] | list[int] | None = None,
+ vision_feature_select_strategy: str | None = None,
+ use_cache: bool | None = None,
+ **kwargs: Unpack[FlashAttentionKwargs],
+ ) -> tuple | LlavaNextModelOutputWithPast:
+ r"""
+ vision_feature_select_strategy (`str`, *optional*, defaults to `"default"`):
+ The feature selection strategy used to select the vision feature from the vision backbone.
+ Can be one of `"default"` or `"full"`. If `"default"`, the CLS token is removed from the vision features.
+ If `"full"`, the full vision features are used.
+ """
+ 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 None:
+ inputs_embeds = self.get_input_embeddings()(input_ids)
+
+ if pixel_values is not None and pixel_values.size(0) > 0:
+ image_features = self.get_image_features(
+ pixel_values,
+ image_sizes,
+ vision_feature_layer=vision_feature_layer,
+ vision_feature_select_strategy=vision_feature_select_strategy,
+ return_dict=True,
+ ).pooler_output
+ image_features = torch.cat(image_features, dim=0).to(inputs_embeds.device, inputs_embeds.dtype)
+ special_image_mask = self.get_placeholder_mask(
+ input_ids, inputs_embeds=inputs_embeds, image_features=image_features
+ )
+ inputs_embeds = inputs_embeds.masked_scatter(special_image_mask, image_features)
+
+ outputs = self.language_model(
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ **kwargs,
+ )
+
+ return LlavaNextModelOutputWithPast(
+ last_hidden_state=outputs.last_hidden_state,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ image_hidden_states=image_features if pixel_values is not None else None,
+ )
+
+
+@auto_docstring(
+ custom_intro="""
+ The LLAVA-NeXT model which consists of a vision backbone and a language model.
+ """
+)
+class LlavaNextForConditionalGeneration(LlavaNextPreTrainedModel, GenerationMixin):
+ _tied_weights_keys = {"lm_head.weight": "model.language_model.embed_tokens.weight"}
+
+ def __init__(self, config: LlavaNextConfig):
+ super().__init__(config)
+ self.model = LlavaNextModel(config)
+ self.lm_head = nn.Linear(config.text_config.hidden_size, config.text_config.vocab_size, bias=False)
+ self.post_init()
+
+ def get_input_embeddings(self):
+ return self.model.get_input_embeddings()
+
+ def set_input_embeddings(self, value):
+ self.model.set_input_embeddings(value)
+
+ def get_output_embeddings(self) -> nn.Module:
+ return self.lm_head
+
+ def pack_image_features(self, image_features, image_sizes, vision_feature_select_strategy, image_newline=None):
+ return self.model.pack_image_features(
+ image_features=image_features,
+ image_sizes=image_sizes,
+ vision_feature_select_strategy=vision_feature_select_strategy,
+ image_newline=image_newline,
+ )
+
+ @merge_with_config_defaults
+ @can_return_tuple
+ @auto_docstring
+ def get_image_features(
+ self,
+ pixel_values: torch.FloatTensor,
+ image_sizes: torch.Tensor,
+ vision_feature_layer: int | list[int] | list[int] | None = None,
+ vision_feature_select_strategy: str | None = None,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | BaseModelOutputWithPooling:
+ r"""
+ pixel_values (`torch.FloatTensor]` of shape `(batch_size, num_patches, channels, height, width)`)
+ The tensors corresponding to the input images.
+ image_sizes (`torch.Tensor` of shape `(num_images, 2)`)
+ Actual image size of each images (H, W).
+ vision_feature_layer (`Union[int, list[int]]`, *optional*):
+ The index of the layer to select the vision feature. If multiple indices are provided,
+ the vision feature of the corresponding indices will be concatenated to form the
+ vision features.
+ vision_feature_select_strategy (`str`, *optional*):
+ The feature selection strategy used to select the vision feature from the vision backbone.
+ Can be one of `"default"` or `"full"`
+ """
+ return self.model.get_image_features(
+ pixel_values=pixel_values,
+ image_sizes=image_sizes,
+ vision_feature_layer=vision_feature_layer,
+ vision_feature_select_strategy=vision_feature_select_strategy,
+ **kwargs,
+ )
+
+ @merge_with_config_defaults
+ @can_return_tuple
+ @auto_docstring
+ def forward(
+ self,
+ input_ids: torch.LongTensor | None = None,
+ pixel_values: torch.FloatTensor | None = None,
+ image_sizes: torch.LongTensor | None = None,
+ attention_mask: torch.Tensor | None = None,
+ position_ids: torch.LongTensor | None = None,
+ past_key_values: Cache | None = None,
+ inputs_embeds: torch.FloatTensor | None = None,
+ vision_feature_layer: int | list[int] | list[int] | None = None,
+ vision_feature_select_strategy: str | None = None,
+ labels: torch.LongTensor | None = None,
+ use_cache: bool | None = None,
+ logits_to_keep: int | torch.Tensor = 0,
+ **kwargs: Unpack[TransformersKwargs],
+ ) -> tuple | LlavaNextCausalLMOutputWithPast:
+ r"""
+ vision_feature_select_strategy (`str`, *optional*, defaults to `"default"`):
+ The feature selection strategy used to select the vision feature from the vision backbone.
+ Can be one of `"default"` or `"full"`. If `"default"`, the CLS token is removed from the vision features.
+ If `"full"`, the full vision features are used.
+ 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]`.
+
+ Example:
+
+ ```python
+ >>> from PIL import Image
+ >>> import httpx
+ >>> from io import BytesIO
+ >>> from transformers import AutoProcessor, LlavaNextForConditionalGeneration
+
+ >>> model = LlavaNextForConditionalGeneration.from_pretrained("llava-hf/llava-v1.6-mistral-7b-hf")
+ >>> processor = AutoProcessor.from_pretrained("llava-hf/llava-v1.6-mistral-7b-hf")
+
+ >>> prompt = "[INST] \nWhat is shown in this image? [/INST]"
+ >>> url = "https://www.ilankelman.org/stopsigns/australia.jpg"
+ >>> with httpx.stream("GET", url) as response:
+ ... image = Image.open(BytesIO(response.read()))
+
+ >>> inputs = processor(images=image, text=prompt, return_tensors="pt")
+
+ >>> # Generate
+ >>> generate_ids = model.generate(**inputs, max_length=30)
+ >>> processor.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]
+ "[INST] \nWhat is shown in this image? [/INST] The image appears to be a radar chart, which is a type of multi-dimensional plot (...)"
+ ```"""
+
+ outputs = self.model(
+ input_ids,
+ pixel_values=pixel_values,
+ image_sizes=image_sizes,
+ vision_feature_layer=vision_feature_layer,
+ vision_feature_select_strategy=vision_feature_select_strategy,
+ attention_mask=attention_mask,
+ position_ids=position_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ use_cache=use_cache,
+ return_dict=True,
+ **kwargs,
+ )
+
+ hidden_states = outputs[0]
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
+
+ loss = None
+ if labels is not None:
+ loss = self.loss_function(
+ logits=logits, labels=labels, vocab_size=self.config.text_config.vocab_size, **kwargs
+ )
+
+ return LlavaNextCausalLMOutputWithPast(
+ loss=loss,
+ logits=logits,
+ past_key_values=outputs.past_key_values,
+ hidden_states=outputs.hidden_states,
+ attentions=outputs.attentions,
+ image_hidden_states=outputs.image_hidden_states,
+ )
+
+ def prepare_inputs_for_generation(
+ self,
+ input_ids,
+ past_key_values=None,
+ inputs_embeds=None,
+ pixel_values=None,
+ image_sizes=None,
+ attention_mask=None,
+ logits_to_keep=None,
+ is_first_iteration=False,
+ **kwargs,
+ ):
+ # Overwritten -- in specific circumstances we don't want to forward image inputs to the model
+
+ model_inputs = super().prepare_inputs_for_generation(
+ input_ids,
+ past_key_values=past_key_values,
+ inputs_embeds=inputs_embeds,
+ attention_mask=attention_mask,
+ logits_to_keep=logits_to_keep,
+ is_first_iteration=is_first_iteration,
+ **kwargs,
+ )
+
+ # Pixel values are used only in the first iteration if available
+ # In subsequent iterations, they are already merged with text and cached
+ # NOTE: first iteration doesn't have to be prefill, it can be the first
+ # iteration with a question and cached system prompt (continue generate from cache)
+ if is_first_iteration or not kwargs.get("use_cache", True):
+ model_inputs["pixel_values"] = pixel_values
+ model_inputs["image_sizes"] = image_sizes
+
+ return model_inputs
+
+
+__all__ = ["LlavaNextForConditionalGeneration", "LlavaNextPreTrainedModel", "LlavaNextModel"]
diff --git a/third_party/transformers/src/transformers/models/llava_next/processing_llava_next.py b/third_party/transformers/src/transformers/models/llava_next/processing_llava_next.py
new file mode 100644
index 0000000000000000000000000000000000000000..5208ae2713ee7fc7b82b3ab911c44b6d33bcb8a4
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/llava_next/processing_llava_next.py
@@ -0,0 +1,232 @@
+# Copyright 2024 The HuggingFace Inc. team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Processor class for LLaVa-NeXT.
+"""
+
+from ...feature_extraction_utils import BatchFeature
+from ...image_processing_utils import select_best_resolution
+from ...image_utils import ImageInput, SizeDict, get_image_size, to_numpy_array
+from ...processing_utils import (
+ MultiModalData,
+ ProcessingKwargs,
+ ProcessorMixin,
+ Unpack,
+)
+from ...tokenization_utils_base import PreTokenizedInput, TextInput
+from ...utils import auto_docstring, logging
+
+
+logger = logging.get_logger(__name__)
+
+
+class LlavaNextProcessorKwargs(ProcessingKwargs, total=False):
+ _defaults = {
+ "text_kwargs": {
+ "padding": False,
+ "return_mm_token_type_ids": False,
+ },
+ "images_kwargs": {
+ "do_pad": True,
+ },
+ }
+
+
+@auto_docstring
+class LlavaNextProcessor(ProcessorMixin):
+ def __init__(
+ self,
+ image_processor=None,
+ tokenizer=None,
+ patch_size=None,
+ vision_feature_select_strategy=None,
+ chat_template=None,
+ image_token="", # set the default and let users change if they have peculiar special tokens in rare cases
+ num_additional_image_tokens=0,
+ **kwargs,
+ ):
+ r"""
+ patch_size (`int`, *optional*):
+ Patch size from the vision tower.
+ vision_feature_select_strategy (`str`, *optional*):
+ The feature selection strategy used to select the vision feature from the vision backbone.
+ Should be same as in model's config
+ image_token (`str`, *optional*, defaults to `""`):
+ Special token used to denote image location.
+ num_additional_image_tokens (`int`, *optional*, defaults to 0):
+ Number of additional tokens added to the image embeddings, such as CLS (+1). If the backbone has no CLS or other
+ extra tokens appended, no need to set this arg.
+ """
+ self.patch_size = patch_size
+ self.num_additional_image_tokens = num_additional_image_tokens
+ self.vision_feature_select_strategy = vision_feature_select_strategy
+ self.image_token = tokenizer.image_token if hasattr(tokenizer, "image_token") else image_token
+ self.image_token_id = (
+ tokenizer.image_token_id
+ if getattr(tokenizer, "image_token_id", None)
+ else tokenizer.convert_tokens_to_ids(self.image_token)
+ )
+ super().__init__(image_processor, tokenizer, chat_template=chat_template)
+
+ @auto_docstring
+ def __call__(
+ self,
+ images: ImageInput | None = None,
+ text: TextInput | PreTokenizedInput | list[TextInput] | list[PreTokenizedInput] = None,
+ **kwargs: Unpack[LlavaNextProcessorKwargs],
+ ) -> BatchFeature:
+ r"""
+ Returns:
+ [`BatchFeature`]: A [`BatchFeature`] with the following fields:
+
+ - **input_ids** -- List of token ids to be fed to a model. Returned when `text` is not `None`.
+ - **attention_mask** -- List of indices specifying which tokens should be attended to by the model (when
+ `return_attention_mask=True` or if *"attention_mask"* is in `self.model_input_names` and if `text` is not
+ `None`).
+ - **pixel_values** -- Pixel values to be fed to a model. Returned when `images` is not `None`.
+ """
+ if images is None and text is None:
+ raise ValueError("You have to specify at least images or text.")
+
+ output_kwargs = self._merge_kwargs(
+ LlavaNextProcessorKwargs,
+ tokenizer_init_kwargs=self.tokenizer.init_kwargs,
+ **kwargs,
+ )
+ if images is not None:
+ image_inputs = self.image_processor(images, **output_kwargs["images_kwargs"])
+ else:
+ image_inputs = {}
+
+ if isinstance(text, str):
+ text = [text]
+ elif not isinstance(text, list) and not isinstance(text[0], str):
+ raise TypeError("Invalid input text. Please provide a string, or a list of strings")
+
+ prompt_strings = text
+ if image_inputs:
+ image_sizes = iter(image_inputs["image_sizes"])
+ height, width = get_image_size(to_numpy_array(image_inputs["pixel_values"][0][0]))
+ prompt_strings = []
+ for sample in text:
+ while self.image_token in sample:
+ image_size = next(image_sizes)
+ if not isinstance(image_size, (list, tuple)):
+ # cast to list to avoid numerical precision errors when calculating unpadding
+ image_size = image_size.tolist()
+ orig_height, orig_width = image_size
+ num_image_tokens = self._get_number_of_features(orig_height, orig_width, height, width)
+ if self.vision_feature_select_strategy == "default":
+ num_image_tokens -= 1
+ sample = sample.replace(self.image_token, "" * num_image_tokens, 1)
+ prompt_strings.append(sample)
+ prompt_strings = [sample.replace("", self.image_token) for sample in prompt_strings]
+
+ return_tensors = output_kwargs["text_kwargs"].pop("return_tensors", None)
+ return_mm_token_type_ids = output_kwargs["text_kwargs"].pop("return_mm_token_type_ids", None)
+ text_inputs = self.tokenizer(prompt_strings, **output_kwargs["text_kwargs"])
+ self._check_special_mm_tokens(prompt_strings, text_inputs, modalities=["image"])
+
+ if return_mm_token_type_ids:
+ text_inputs["mm_token_type_ids"] = self.create_mm_token_type_ids(text_inputs["input_ids"])
+ return BatchFeature(data={**text_inputs, **image_inputs}, tensor_type=return_tensors)
+
+ def _get_number_of_features(self, orig_height: int, orig_width: int, height: int, width: int) -> int:
+ image_grid_pinpoints = self.image_processor.image_grid_pinpoints
+
+ height_best_resolution, width_best_resolution = select_best_resolution(
+ [orig_height, orig_width], image_grid_pinpoints
+ )
+ scale_height, scale_width = height_best_resolution // height, width_best_resolution // width
+
+ patches_height = height // self.patch_size
+ patches_width = width // self.patch_size
+ unpadded_features, newline_features = self._get_unpadded_features(
+ orig_height, orig_width, patches_height, patches_width, scale_height, scale_width
+ )
+ # The base patch covers the entire image (+1 for the CLS)
+ base_features = patches_height * patches_width + self.num_additional_image_tokens
+ num_image_tokens = unpadded_features + newline_features + base_features
+ return num_image_tokens
+
+ def _get_unpadded_features(self, height, width, patches_height, patches_width, scale_height, scale_width):
+ """
+ Get number of features for a given image with height/width. LLaVA-NeXT is different from LLaVA
+ because it divided each image into patches depending on its resolution. Therefore we need to calculate how many
+ patches an image is divided into and get the number of features from that.
+ """
+ current_height = patches_height * scale_height
+ current_width = patches_width * scale_width
+
+ original_aspect_ratio = width / height
+ current_aspect_ratio = current_width / current_height
+ if original_aspect_ratio > current_aspect_ratio:
+ new_height = int(round(height * (current_width / width), 7))
+ padding = (current_height - new_height) // 2
+ current_height -= padding * 2
+ else:
+ new_width = int(round(width * (current_height / height), 7))
+ padding = (current_width - new_width) // 2
+ current_width -= padding * 2
+
+ unpadded_features = current_height * current_width
+ newline_features = current_height
+ return (unpadded_features, newline_features)
+
+ def _get_num_multimodal_tokens(self, image_sizes=None, **kwargs):
+ """
+ Computes the number of placeholder tokens needed for multimodal inputs with the given sizes.
+ Args:
+ image_sizes (list[list[str]], *optional*):
+ The input sizes formatted as (height, width) per each image.
+ Returns:
+ `MultiModalData`: A `MultiModalData` object holding number of tokens per each of the provided
+ input modalities, along with other useful data.
+ """
+ vision_data = {}
+ if image_sizes is not None:
+ images_kwargs = LlavaNextProcessorKwargs._defaults.get("images_kwargs", {})
+ images_kwargs.update(kwargs)
+
+ size = images_kwargs.get("size", None) or self.image_processor.size
+ if isinstance(size, SizeDict):
+ size = (
+ (size.shortest_edge, size.shortest_edge)
+ if size.shortest_edge is not None
+ else (min(size.height, size.width), min(size.height, size.width))
+ )
+ else:
+ size = (
+ (size["shortest_edge"], size["shortest_edge"])
+ if "shortest_edge" in size
+ else (min(size["height"], size["width"]), min(size["height"], size["width"]))
+ )
+ processed_height, processed_width = size
+
+ batch_num_image_tokens = []
+ num_image_patches = [1] * len(image_sizes) # llava-next doesn't batch pixels as Idefics, thus `1` patch`
+ for image_size in image_sizes:
+ orig_height, orig_width = image_size
+ num_image_tokens = self._get_number_of_features(
+ orig_height, orig_width, processed_height, processed_width
+ )
+ if self.vision_feature_select_strategy == "default":
+ num_image_tokens -= 1
+ batch_num_image_tokens.append(num_image_tokens)
+ vision_data.update({"num_image_tokens": batch_num_image_tokens, "num_image_patches": num_image_patches})
+
+ return MultiModalData(**vision_data)
+
+
+__all__ = ["LlavaNextProcessor"]
diff --git a/third_party/transformers/src/transformers/models/llava_next_video/__init__.py b/third_party/transformers/src/transformers/models/llava_next_video/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e3632c7a2a14278d69c129e6431134314cf59ee5
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/llava_next_video/__init__.py
@@ -0,0 +1,29 @@
+# Copyright 2024 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import TYPE_CHECKING
+
+from ...utils import _LazyModule
+from ...utils.import_utils import define_import_structure
+
+
+if TYPE_CHECKING:
+ from .configuration_llava_next_video import *
+ from .image_processing_llava_next_video import *
+ from .modeling_llava_next_video import *
+ from .processing_llava_next_video import *
+else:
+ import sys
+
+ _file = globals()["__file__"]
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
diff --git a/third_party/transformers/src/transformers/models/llava_next_video/configuration_llava_next_video.py b/third_party/transformers/src/transformers/models/llava_next_video/configuration_llava_next_video.py
new file mode 100644
index 0000000000000000000000000000000000000000..044b20495aeffaf7384852321f0e5f82948527ae
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/llava_next_video/configuration_llava_next_video.py
@@ -0,0 +1,120 @@
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# This file was automatically generated from src/transformers/models/llava_next_video/modular_llava_next_video.py.
+# Do NOT edit this file manually as any edits will be overwritten by the generation of
+# the file from the modular. If any change should be done, please apply the change to the
+# modular_llava_next_video.py file directly. One of our CI enforces this.
+# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
+# Copyright 2024 HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from typing import Literal
+
+from huggingface_hub.dataclasses import strict
+
+from ...configuration_utils import PreTrainedConfig
+from ...utils import auto_docstring
+from ..auto import CONFIG_MAPPING, AutoConfig
+
+
+@auto_docstring(checkpoint="llava-hf/LLaVA-NeXT-Video-7B-hf")
+@strict
+class LlavaNextVideoConfig(PreTrainedConfig):
+ r"""
+ image_grid_pinpoints (`List`, *optional*, defaults to `[[336, 672], [672, 336], [672, 672], [1008, 336], [336, 1008]]`):
+ A list of possible resolutions to use for processing high resolution images. Each item in the list should be a tuple or list
+ of the form `(height, width)`.
+ spatial_pool_mode (`str`, *optional*, defaults to `"average"`):
+ Pooling mode to use for videos. Can be "average", "max" or "conv".
+ spatial_pool_stride (`int`, *optional*, defaults to 2):
+ Stride used in the pooling layer for videos.
+
+ Example:
+
+ ```python
+ >>> from transformers import LlavaNextVideoForConditionalGeneration, LlavaNextVideoConfig, CLIPVisionConfig, LlamaConfig
+
+ >>> # Initializing a CLIP-vision config
+ >>> vision_config = CLIPVisionConfig()
+
+ >>> # Initializing a Llama config
+ >>> text_config = LlamaConfig()
+
+ >>> configuration = LlavaNextVideoConfig(vision_config, text_config)
+
+ >>> model = LlavaNextVideoForConditionalGeneration(configuration)
+
+ >>> # Accessing the model configuration
+ >>> configuration = model.config
+ ```"""
+
+ model_type = "llava_next_video"
+ attribute_map = {
+ "image_token_id": "image_token_index",
+ "video_token_id": "video_token_index",
+ }
+ sub_configs = {"text_config": AutoConfig, "vision_config": AutoConfig}
+
+ vision_config: dict | PreTrainedConfig | None = None
+ text_config: dict | PreTrainedConfig | None = None
+ image_token_index: int = 32001
+ video_token_index: int = 32000
+ projector_hidden_act: str = "gelu"
+ vision_feature_select_strategy: Literal["default", "full"] = "default"
+ vision_feature_layer: int | list[int] = -2
+ multimodal_projector_bias: bool = True
+ tie_word_embeddings: bool = False
+ image_grid_pinpoints: list | None = None
+ spatial_pool_mode: str = "average"
+ spatial_pool_stride: int = 2
+ image_seq_length: int = 576
+ video_seq_length: int = 288
+
+ def __post_init__(self, **kwargs):
+ if isinstance(self.vision_config, dict):
+ self.vision_config["model_type"] = self.vision_config.get("model_type", "clip_vision_model")
+ self.vision_config = CONFIG_MAPPING[self.vision_config["model_type"]](**self.vision_config)
+ elif self.vision_config is None:
+ self.vision_config = CONFIG_MAPPING["clip_vision_model"](
+ intermediate_size=4096,
+ hidden_size=1024,
+ patch_size=14,
+ image_size=336,
+ num_hidden_layers=24,
+ num_attention_heads=16,
+ vocab_size=32000,
+ projection_dim=768,
+ )
+
+ if isinstance(self.text_config, dict):
+ self.text_config["model_type"] = self.text_config.get("model_type", "llama")
+ self.text_config = CONFIG_MAPPING[self.text_config["model_type"]](**self.text_config)
+ elif self.text_config is None:
+ self.text_config = CONFIG_MAPPING["llama"]()
+
+ self.image_grid_pinpoints = (
+ self.image_grid_pinpoints
+ if self.image_grid_pinpoints is not None
+ else [[336, 672], [672, 336], [672, 672], [1008, 336], [336, 1008]]
+ )
+
+ # The default value is `False` but this config is used with many model types
+ # Attr `tie_word_embeddings` was saved in text config for those models, so we
+ # need an ugly workaround and forward-pass the attr from text config
+ if not self.tie_word_embeddings and self.text_config.tie_word_embeddings:
+ self.tie_word_embeddings = self.text_config.tie_word_embeddings
+
+ super().__post_init__(**kwargs)
+
+
+__all__ = ["LlavaNextVideoConfig"]
diff --git a/third_party/transformers/src/transformers/models/llava_next_video/convert_llava_next_video_weights_to_hf.py b/third_party/transformers/src/transformers/models/llava_next_video/convert_llava_next_video_weights_to_hf.py
new file mode 100644
index 0000000000000000000000000000000000000000..7b348f6f42234ea65c2a35ca7351a33de175a0d2
--- /dev/null
+++ b/third_party/transformers/src/transformers/models/llava_next_video/convert_llava_next_video_weights_to_hf.py
@@ -0,0 +1,275 @@
+# Copyright 2024 The HuggingFace Inc. team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Convert LLaVa-NeXT-Video checkpoints from the original repository.
+
+URL: https://github.com/LLaVA-VL/LLaVA-NeXT/tree/inference
+"""
+
+import argparse
+import glob
+import json
+from pathlib import Path
+
+import torch
+from huggingface_hub import hf_hub_download, snapshot_download
+from safetensors import safe_open
+
+from transformers import (
+ AddedToken,
+ AutoConfig,
+ AutoTokenizer,
+ LlavaNextImageProcessor,
+ LlavaNextVideoConfig,
+ LlavaNextVideoForConditionalGeneration,
+ LlavaNextVideoProcessor,
+ LlavaNextVideoVideoProcessor,
+)
+
+
+KEYS_TO_MODIFY_MAPPING = {
+ "model.vision_tower.": "",
+ ".vision_resampler": "", # all lmms-lab models do avg pooling, so no vision_resampler
+ "model.mm_projector": "multi_modal_projector",
+ "model": "model.model",
+ "vision_model.model": "vision_model",
+ "lm_head": "language_model.lm_head",
+ "model.model": "language_model.model",
+ "multi_modal_projector.0": "multi_modal_projector.linear_1",
+ "multi_modal_projector.2": "multi_modal_projector.linear_2",
+ "language_model.model.image_newline": "image_newline",
+}
+
+# {{SYSTEM_PROMPT}} USER: \n{{PROMPT}} ASSISTANT:" assistant end with "
"
+chat_vicuna = (
+ "{% for message in messages %}"
+ "{% if message['role'] == 'system' %}"
+ "{{ message['content'][0]['text'] }}"
+ "{% else %}"
+ "{{ message['role'].upper() + ': '}}"
+ "{% endif %}"
+ "{# Render all images first #}"
+ "{% for content in message['content'] | selectattr('type', 'equalto', 'image') %}"
+ "{{ '\n' }}"
+ "{% endfor %}"
+ "{# Render all text next #}"
+ "{% for content in message['content'] | selectattr('type', 'equalto', 'text') %}"
+ "{{ content['text'] + ' '}}"
+ "{% endfor %}"
+ "{% endfor %}"
+ "{% if add_generation_prompt %}"
+ "{{ 'ASSISTANT:' }}"
+ "{% endif %}"
+)
+
+# "[INST] \nWhat is shown in this image? [/INST]" assistant end with " "
+chat_mistral = (
+ "{% for message in messages %}"
+ "{% if message['role'] == 'user' %}"
+ "{{ '[INST] ' }}"
+ "{# Render all images first #}"
+ "{% for content in message['content'] | selectattr('type', 'equalto', 'image') %}"
+ "{{ '\n' }}"
+ "{% endfor %}"
+ "{# Render all text next #}"
+ "{% for content in message['content'] | selectattr('type', 'equalto', 'text') %}"
+ "{{ content['text'] }}"
+ "{% endfor %}"
+ "{{' [/INST]' }}"
+ "{% elif message['role'] == 'assistant' %}"
+ r"{{ ' ' + message['content'][0]['text'] + '<\s> '}}"
+ "{% else %}"
+ "{{ raise_exception('Only user and assistant roles are supported!') }}"
+ "{% endif %}"
+ "{% endfor %}"
+)
+
+# "<|im_start|>system\nAnswer the questions.<|im_end|><|im_start|>user\n\nWhat is shown in this image?<|im_end|><|im_start|>assistant\n"
+chat_yi = (
+ "{% for message in messages %}"
+ "{{'<|im_start|>' + message['role'] + '\n'}}"
+ "{# Render all images first #}"
+ "{% for content in message['content'] | selectattr('type', 'equalto', 'image') %}"
+ "{{ '\n' }}"
+ "{% endfor %}"
+ "{# Render all text next #}"
+ "{% for content in message['content'] | selectattr('type', 'equalto', 'text') %}"
+ "{{ content['text'] }}"
+ "{% endfor %}"
+ "{{'<|im_end|>' + '\n'}}"
+ "{% endfor %}"
+ "{% if add_generation_prompt %}"
+ "{{ '<|im_start|>assistant\n' }}"
+ "{% endif %}"
+)
+
+model2template = {
+ "lmms-lab/LLaVA-NeXT-Video-7B-32K": chat_mistral,
+ "lmms-lab/LLaVA-NeXT-Video-7B": chat_vicuna,
+ "lmms-lab/LLaVA-NeXT-Video-7B-DPO": chat_vicuna,
+ "lmms-lab/LLaVA-NeXT-Video-34B": chat_yi,
+ "lmms-lab/LLaVA-NeXT-Video-34B-DPO": chat_yi,
+}
+
+
+def load_original_state_dict(model_id):
+ directory_path = snapshot_download(repo_id=model_id, allow_patterns=["*.safetensors"])
+
+ original_state_dict = {}
+ for path in glob.glob(f"{directory_path}/*"):
+ if path.endswith(".safetensors"):
+ with safe_open(path, framework="pt", device="cpu") as f:
+ for key in f.keys():
+ original_state_dict[key] = f.get_tensor(key)
+
+ return original_state_dict
+
+
+def convert_state_dict_to_hf(state_dict):
+ new_state_dict = {}
+ for key, value in state_dict.items():
+ if key.endswith(".inv_freq"):
+ continue
+ for key_to_modify, new_key in KEYS_TO_MODIFY_MAPPING.items():
+ if key_to_modify in key:
+ key = key.replace(key_to_modify, new_key)
+
+ new_state_dict[key] = value.to(torch.bfloat16)
+ return new_state_dict
+
+
+def convert_llava_to_hf(model_id, pytorch_dump_folder_path, push_to_hub=False):
+ # load original config
+ filepath = hf_hub_download(repo_id=model_id, filename="config.json", repo_type="model")
+ with open(filepath) as f:
+ data = json.load(f)
+ print(data)
+
+ if model_id == "lmms-lab/LLaVA-NeXT-Video-7B-32K":
+ text_model_id = "mistralai/Mistral-7B-Instruct-v0.2"
+ video_token_id = 32000
+ image_token_id = 32001
+ overwrite_text_config = {}
+ elif model_id in ["lmms-lab/LLaVA-NeXT-Video-7B", "lmms-lab/LLaVA-NeXT-Video-7B-DPO"]:
+ text_model_id = "lmsys/vicuna-7b-v1.5"
+ video_token_id = 32000
+ image_token_id = 32001
+ overwrite_text_config = {"factor": 2.0, "type": "linear"}
+ elif model_id in ["lmms-lab/LLaVA-NeXT-Video-34B", "lmms-lab/LLaVA-NeXT-Video-34B-DPO"]:
+ text_model_id = "NousResearch/Nous-Hermes-2-Yi-34B"
+ video_token_id = 64000
+ image_token_id = 64001
+ overwrite_text_config = {}
+ else:
+ raise ValueError("Incorrect checkpoint referenced. Text model-id not identified!")
+
+ vision_model_id = data["mm_vision_tower"]
+
+ torch.set_default_dtype(torch.bfloat16)
+ text_config = AutoConfig.from_pretrained(text_model_id)
+ text_config = text_config.to_dict()
+ text_config.update(overwrite_text_config)
+
+ tokenizer = AutoTokenizer.from_pretrained(text_model_id, use_fast=True, padding_side="left")
+ tokenizer.add_tokens(AddedToken("