d1-2B / modeling_lfm3_vl.py
Raw History Blame Contribute Delete
46.1 kB
# Vendored from transformers' LFM3 code (not in a release yet) at 69da1b47a11e362fbb15bfe5d5c8da0790c688c5, src/transformers/models/lfm3_vl/modeling_lfm3_vl.py:
# its relative imports made absolute, nothing else changed.
# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
# This file was automatically generated from src/transformers/models/lfm3_vl/modular_lfm3_vl.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_lfm3_vl.py file directly. One of our CI enforces this.
# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
# Copyright 2026 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 transformers.activations import ACT2FN
from transformers.cache_utils import Cache
from transformers.configuration_utils import PreTrainedConfig
from transformers.generation import GenerationMixin
from transformers.integrations import use_kernel_forward_from_hub
from transformers.masking_utils import create_causal_mask, create_recurrent_attention_mask, create_sliding_window_causal_mask
from transformers.modeling_layers import GradientCheckpointingLayer
from transformers.modeling_outputs import BaseModelOutputWithPast, BaseModelOutputWithPooling, CausalLMOutputWithPast
from transformers.modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
from transformers.processing_utils import Unpack
from transformers.utils import (
TransformersKwargs,
auto_docstring,
can_return_tuple,
torch_compilable_check,
)
from transformers.utils.generic import (
accepts_precomputed_kwargs,
is_flash_attention_requested,
maybe_autocast,
merge_with_config_defaults,
)
from transformers.utils.output_capturing import OutputRecorder, capture_outputs
from transformers.vision_utils import get_max_seqlen, get_vision_attention_seqlens, get_vision_position_ids
from transformers.models.auto import AutoModel
from .configuration_lfm3_vl import Lfm3VlConfig, Lfm3VlVisionConfig
@use_kernel_forward_from_hub("RMSNorm")
class Lfm3VlRMSNorm(nn.Module):
def __init__(self, hidden_size, eps: float = 1e-6) -> None:
"""
Lfm3VlRMSNorm 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 Lfm3VlMLP(nn.Module):
def __init__(self, config: Lfm3VlConfig):
super().__init__()
self.w1 = nn.Linear(config.hidden_size, config.intermediate_size, bias=False)
self.w3 = nn.Linear(config.hidden_size, config.intermediate_size, bias=False)
self.w2 = nn.Linear(config.intermediate_size, config.hidden_size, bias=False)
def forward(self, x):
return self.w2(F.silu(self.w1(x)) * self.w3(x))
class Lfm3VlVisionRotaryEmbedding(nn.Module):
"""
Simple axial 2D rope with same freqs used for H and W grids. The freqs are
pre-computed using `head-dim//4` which is later used to concat H and W positions.
The final angles rotate over the whole head dim, no partial rotation involved.
"""
def __init__(self, config: Lfm3VlVisionConfig):
super().__init__()
self.config = config
self.rope_type = self.config.rope_parameters["rope_type"]
rope_init_fn: Callable = self.compute_axial_rope_parameters
if self.rope_type != "axial":
raise ValueError(f"{self.__class__.__name__} supports only axial rope, but requested {self.rope_type}")
inv_freq, self.attention_scaling = rope_init_fn(self.config)
self.inv_freq = nn.Buffer(inv_freq, persistent=False)
self.original_inv_freq = nn.Buffer(inv_freq.clone(), persistent=False)
@staticmethod
def compute_axial_rope_parameters(config: Lfm3VlVisionConfig, **kwargs) -> tuple[torch.Tensor, float]:
"""Inverse frequencies of the axial RoPE over half the head dimension per axis, with an attention scaling of 1."""
base = config.rope_parameters["rope_theta"]
spatial_dim = config.hidden_size // config.num_attention_heads // 2
inv_freq = 1.0 / (base ** (torch.arange(0, spatial_dim, 2, dtype=torch.float) / spatial_dim))
return inv_freq, 1.0
@torch.no_grad()
def forward(self, x, position_ids):
# position_ids: (2, N) — row 0 = h coords, row 1 = w coords
position_ids_expanded = position_ids[..., None].float()
device_type = x.device.type if isinstance(x.device.type, str) else "cpu"
with maybe_autocast(device_type=device_type, enabled=False):
freqs = position_ids_expanded * self.inv_freq.float()
cos = freqs.cos() * self.attention_scaling
sin = freqs.sin() * self.attention_scaling
cos = self.recomposition_frequencies(cos)
sin = self.recomposition_frequencies(sin)
return cos, sin
def recomposition_frequencies(self, freq):
"""
Recompose the frequencies into the final spatial layout used per each grid.
"""
freq_h, freq_w = freq[:, 0], freq[:, 1]
freq_hw = torch.cat([freq_h, freq_w], dim=-1)
return torch.cat([freq_hw, freq_hw], dim=-1)
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_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
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 Lfm3VlVisionAttention(nn.Module):
"""Bidirectional grouped-query attention over the patches of one frame, with RMSNorm on the query and key heads."""
def __init__(self, config: Lfm3VlVisionConfig):
super().__init__()
self.config = config
self.num_heads = config.num_attention_heads
self.num_key_value_heads = config.num_key_value_heads
self.num_key_value_groups = config.num_attention_heads // config.num_key_value_heads
self.head_dim = config.hidden_size // config.num_attention_heads
self.scaling = self.head_dim**-0.5
self.attention_dropout = 0.0
self.is_causal = False
self.q_proj = nn.Linear(config.hidden_size, self.num_heads * self.head_dim, bias=False)
self.k_proj = nn.Linear(config.hidden_size, self.num_key_value_heads * self.head_dim, bias=False)
self.v_proj = nn.Linear(config.hidden_size, self.num_key_value_heads * self.head_dim, bias=False)
self.out_proj = nn.Linear(self.num_heads * self.head_dim, config.hidden_size, bias=False)
self.q_layernorm = Lfm3VlRMSNorm(self.head_dim, eps=config.norm_eps)
self.k_layernorm = Lfm3VlRMSNorm(self.head_dim, eps=config.norm_eps)
def forward(
self,
hidden_states: torch.Tensor,
cu_seqlens: torch.Tensor,
position_embeddings: tuple[torch.Tensor, torch.Tensor],
max_seqlen: int | None = None,
frame_shapes: list[list[int]] | None = None,
**kwargs,
) -> torch.Tensor:
seq_length = hidden_states.shape[0]
query_states = self.q_layernorm(self.q_proj(hidden_states).view(seq_length, self.num_heads, self.head_dim))
key_states = self.k_layernorm(
self.k_proj(hidden_states).view(seq_length, self.num_key_value_heads, self.head_dim)
)
value_states = self.v_proj(hidden_states).view(seq_length, self.num_key_value_heads, self.head_dim)
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):
max_seqlen = get_max_seqlen(cu_seqlens, self.config, kwargs={"max_seqlen": max_seqlen})
attn_output, attn_weights = attention_interface(
self,
query_states,
key_states,
value_states,
attention_mask=None,
scaling=self.scaling,
dropout=0.0,
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: frames of one shape (every video, equal-size images) attend as one batch, mixed
# shapes frame by frame. `frame_shapes` gives the lengths without a device sync.
if frame_shapes is not None:
lengths = [height * width for height, width in frame_shapes]
else:
lengths = (cu_seqlens[1:] - cu_seqlens[:-1]).tolist()
if len(set(lengths)) == 1:
query_states, key_states, value_states = (
tensor.reshape(-1, len(lengths), lengths[0], self.head_dim).transpose(0, 1)
for tensor in (query_states, key_states, value_states)
)
attn_output, attn_weights = attention_interface(
self,
query_states,
key_states,
value_states,
attention_mask=None,
scaling=self.scaling,
dropout=0.0,
is_causal=False,
**kwargs,
)
else:
splits = [torch.split(tensor, lengths, dim=2) for tensor in (query_states, key_states, value_states)]
attn_outputs, attn_weights = [], []
for q, k, v in zip(*splits):
output, weights = attention_interface(
self,
q,
k,
v,
attention_mask=None,
scaling=self.scaling,
dropout=0.0,
is_causal=False,
**kwargs,
)
attn_outputs.append(output)
attn_weights.append(weights)
attn_output = torch.cat(attn_outputs, dim=1)
# frames of different shapes have differently sized maps: one entry per frame, or None under sdpa
attn_weights = None if attn_weights[0] is None else tuple(attn_weights)
attn_output = attn_output.reshape(seq_length, -1).contiguous()
attn_output = self.out_proj(attn_output)
return attn_output, attn_weights
class Lfm3VlVisionConv2d(nn.Module):
"""Gated depthwise 2D convolution over the patch grid of each frame."""
def __init__(self, config: Lfm3VlVisionConfig):
super().__init__()
self.spatial_merge_size = config.spatial_merge_size
self.in_proj = nn.Linear(config.hidden_size, 3 * config.hidden_size, bias=False)
self.spatial_conv = nn.Conv2d(
config.hidden_size,
config.hidden_size,
kernel_size=config.conv_kernel_size,
padding=config.conv_kernel_size // 2,
groups=config.hidden_size,
bias=False,
)
self.out_proj = nn.Linear(config.hidden_size, config.hidden_size, bias=False)
def forward(self, hidden_states: torch.Tensor, frame_shapes: list[list[int]]):
B, C, x = self.in_proj(hidden_states).chunk(3, dim=-1)
gated = B * x
merge = self.spatial_merge_size
num_frames = len(frame_shapes)
if len(set(map(tuple, frame_shapes))) == 1:
# frames of one shape (every video, equal-size images) convolve as one batch: merge-block order -> raster
# grid, convolve, back
height, width = frame_shapes[0]
frames = gated.view(num_frames, height // merge, width // merge, merge, merge, -1)
frames = frames.permute(0, 5, 1, 3, 2, 4).reshape(num_frames, -1, height, width)
frames = self.spatial_conv(frames)
frames = frames.view(num_frames, -1, height // merge, merge, width // merge, merge).permute(
0, 2, 4, 3, 5, 1
)
conv_output = frames.reshape(gated.shape[0], -1)
else:
conv_outputs = []
lengths = [height * width for height, width in frame_shapes]
for frame, (height, width) in zip(torch.split(gated, lengths, dim=0), frame_shapes):
frame = frame.view(height // merge, width // merge, merge, merge, -1)
frame = frame.permute(4, 0, 2, 1, 3).reshape(1, -1, height, width)
frame = self.spatial_conv(frame)
frame = frame.view(-1, height // merge, merge, width // merge, merge).permute(1, 3, 2, 4, 0)
conv_outputs.append(frame.reshape(height * width, -1))
conv_output = torch.cat(conv_outputs, dim=0)
return self.out_proj(C * conv_output)
class Lfm3VlVisionLayer(GradientCheckpointingLayer):
def __init__(self, config: Lfm3VlVisionConfig, layer_idx: int):
super().__init__()
self.is_attention_layer = config.layer_types[layer_idx] == "full_attention"
if self.is_attention_layer:
self.self_attn = Lfm3VlVisionAttention(config)
else:
self.conv = Lfm3VlVisionConv2d(config)
self.feed_forward = Lfm3VlMLP(config)
self.operator_norm = Lfm3VlRMSNorm(config.hidden_size, eps=config.norm_eps)
self.ffn_norm = Lfm3VlRMSNorm(config.hidden_size, eps=config.norm_eps)
def forward(
self,
hidden_states: torch.Tensor,
cu_seqlens: torch.Tensor,
frame_shapes: list[list[int]],
position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
**kwargs,
) -> torch.Tensor:
if self.is_attention_layer:
operator_output, _ = self.self_attn(
self.operator_norm(hidden_states),
cu_seqlens=cu_seqlens,
position_embeddings=position_embeddings,
frame_shapes=frame_shapes,
**kwargs,
)
else:
operator_output = self.conv(self.operator_norm(hidden_states), frame_shapes)
hidden_states = hidden_states + operator_output
hidden_states = hidden_states + self.feed_forward(self.ffn_norm(hidden_states))
return hidden_states
def create_masks_by_layer_type(
config: PreTrainedConfig,
inputs_embeds: torch.Tensor,
attention_mask: torch.Tensor | None,
past_key_values: Cache | None,
position_ids: torch.Tensor | None = None,
) -> dict:
"""The cache sizes each mask from the layer it is asked about, and the generic mask utilities ask about the first
non-sliding layer, which for a leading `conv` layer redirects to the first attention layer of any type: name the
first layer of each mask's own type instead."""
config = config.get_text_config()
mask_kwargs = {
"config": config,
"inputs_embeds": inputs_embeds,
"attention_mask": attention_mask,
"past_key_values": past_key_values,
"position_ids": position_ids,
}
layer_types, causal_mask_mapping = config.layer_types, {}
if "full_attention" in layer_types:
layer_idx = layer_types.index("full_attention")
causal_mask_mapping["full_attention"] = create_causal_mask(**mask_kwargs, layer_idx=layer_idx)
if "sliding_attention" in layer_types:
layer_idx = layer_types.index("sliding_attention")
causal_mask_mapping["sliding_attention"] = create_sliding_window_causal_mask(
**mask_kwargs, layer_idx=layer_idx
)
if "conv" in layer_types:
causal_mask_mapping["conv"] = create_recurrent_attention_mask(**mask_kwargs)
return causal_mask_mapping
@auto_docstring
class Lfm3VlPreTrainedModel(PreTrainedModel):
config: Lfm3VlConfig
base_model_prefix = "model"
input_modalities = ("image", "video", "text")
supports_gradient_checkpointing = True
_skip_keys_device_placement = ["past_key_values"]
_supports_flash_attn = True
_supports_sdpa = True
# the cached decode step traces without graph breaks, like the text model
_can_compile_fullgraph = True
_supports_flex_attn = True
_supports_attention_backend = True
_no_split_modules = ["Lfm3VlVisionLayer"]
@staticmethod
def create_masks_for_generate(
config: PreTrainedConfig,
inputs_embeds: torch.Tensor,
attention_mask: torch.Tensor | None,
past_key_values: Cache | None,
position_ids: torch.Tensor | None = None,
**kwargs,
) -> dict:
# the static-cache masks of the text model are sized by layer type, not by the generic first attention layer
return create_masks_by_layer_type(config, inputs_embeds, attention_mask, past_key_values, position_ids)
def get_vision_frame_shapes(grid_thw: torch.Tensor) -> list[list[int]]:
return grid_thw[:, 1:].repeat_interleave(grid_thw[:, 0], dim=0).tolist()
@auto_docstring(
custom_intro="""
The LFM3-VL vision encoder: a stack of gated convolution and bidirectional attention layers over the patches of
each image or video frame, embedded by a linear patchifier.
"""
)
class Lfm3VlVisionModel(Lfm3VlPreTrainedModel):
config: Lfm3VlVisionConfig
input_modalities = ("image", "video")
_input_embed_layer = "patchifier"
_can_record_outputs = {
"hidden_states": Lfm3VlVisionLayer,
"attentions": OutputRecorder(Lfm3VlVisionAttention, index=1),
}
def __init__(self, config: Lfm3VlVisionConfig):
super().__init__(config)
self.spatial_merge_size = config.spatial_merge_size
patch_dim = config.in_channels * config.temporal_patch_size * config.patch_size**2
self.patchifier = nn.Linear(patch_dim, config.hidden_size, bias=False)
self.embedding_norm = Lfm3VlRMSNorm(config.hidden_size, eps=config.norm_eps)
self.rotary_pos_emb = Lfm3VlVisionRotaryEmbedding(config)
self.layers = nn.ModuleList(
[Lfm3VlVisionLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
)
self.gradient_checkpointing = False
self.post_init()
@merge_with_config_defaults
@capture_outputs
@auto_docstring
def forward(
self, pixel_values: torch.Tensor, grid_thw: torch.Tensor, **kwargs: Unpack[TransformersKwargs]
) -> BaseModelOutputWithPooling:
r"""
pixel_values (`torch.Tensor` of shape `(num_patches, in_channels * temporal_patch_size * patch_size**2)`):
The flattened patches of every image or video, grouped by merge block.
grid_thw (`torch.LongTensor` of shape `(num_images_or_videos, 3)`):
The temporal, height and width dimensions of the patch grid of each image or video.
"""
position_ids = get_vision_position_ids(grid_thw, self.spatial_merge_size, kwargs=kwargs)
cu_seqlens, max_seqlen = get_vision_attention_seqlens(grid_thw, self.config, kwargs=kwargs)
frame_shapes = get_vision_frame_shapes(grid_thw)
hidden_states = self.patchifier(pixel_values.to(self.patchifier.weight.dtype))
hidden_states = self.embedding_norm(hidden_states)
position_embeddings = self.rotary_pos_emb(hidden_states, position_ids)
for layer in self.layers:
hidden_states = layer(
hidden_states,
cu_seqlens=cu_seqlens,
frame_shapes=frame_shapes,
max_seqlen=max_seqlen,
position_embeddings=position_embeddings,
**kwargs,
)
return BaseModelOutputWithPooling(last_hidden_state=hidden_states)
class Lfm3VlMultiModalProjector(nn.Module):
"""Merges each block of `downsample_factor**2` vision tokens into one language token."""
def __init__(self, config: Lfm3VlConfig):
super().__init__()
self.downsample_factor = config.downsample_factor
self.layer_norm = nn.LayerNorm(config.vision_config.hidden_size, eps=config.projector_layer_norm_eps)
self.linear_1 = nn.Linear(
config.vision_config.hidden_size * config.downsample_factor**2,
config.projector_hidden_size,
bias=config.projector_bias,
)
self.act = ACT2FN[config.projector_hidden_act]
self.linear_2 = nn.Linear(
config.projector_hidden_size, config.text_config.hidden_size, bias=config.projector_bias
)
def forward(self, image_features: torch.Tensor):
hidden_states = self.layer_norm(image_features).reshape(-1, self.linear_1.in_features)
hidden_states = self.linear_1(hidden_states)
hidden_states = self.act(hidden_states)
hidden_states = self.linear_2(hidden_states)
return hidden_states
@auto_docstring(
custom_intro="""
Base class for Lfm3Vl outputs, with hidden states and attentions.
"""
)
@dataclass
class Lfm3VlModelOutputWithPast(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 `(num_image_tokens, hidden_size)`.
image_hidden_states of the model produced by the vision encoder and after projecting the last hidden state.
video_hidden_states (`torch.FloatTensor`, *optional*):
A `torch.FloatTensor` of size `(num_video_tokens, hidden_size)`.
video_hidden_states of the model produced by the vision encoder and after projecting the last hidden state.
"""
image_hidden_states: torch.FloatTensor | None = None
video_hidden_states: torch.FloatTensor | None = None
@auto_docstring(
custom_intro="""
Base class for Lfm3Vl causal language model (or autoregressive) outputs.
"""
)
@dataclass
class Lfm3VlCausalLMOutputWithPast(CausalLMOutputWithPast):
r"""
image_hidden_states (`torch.FloatTensor`, *optional*):
A `torch.FloatTensor` of size `(num_image_tokens, hidden_size)`.
image_hidden_states of the model produced by the vision encoder and after projecting the last hidden state.
video_hidden_states (`torch.FloatTensor`, *optional*):
A `torch.FloatTensor` of size `(num_video_tokens, hidden_size)`.
video_hidden_states of the model produced by the vision encoder and after projecting the last hidden state.
"""
image_hidden_states: torch.FloatTensor | None = None
video_hidden_states: torch.FloatTensor | None = None
@auto_docstring(
custom_intro="""
The Lfm3Vl model which consists of a vision backbone and a language model, without a language modeling head.
"""
)
class Lfm3VlModel(Lfm3VlPreTrainedModel):
def __init__(self, config: Lfm3VlConfig):
super().__init__(config)
self.vision_tower = AutoModel.from_config(config.vision_config)
self.multi_modal_projector = Lfm3VlMultiModalProjector(config)
self.language_model = AutoModel.from_config(config.text_config)
self.post_init()
@accepts_precomputed_kwargs(modality="image")
@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_grid_thw: torch.LongTensor,
**kwargs: Unpack[TransformersKwargs],
) -> tuple | BaseModelOutputWithPooling:
r"""
pixel_values (`torch.FloatTensor` of shape `(num_patches, in_channels * temporal_patch_size * patch_size**2)`):
The flattened patches of every image, grouped by merge block.
image_grid_thw (`torch.LongTensor` of shape `(num_images, 3)`):
The temporal, height and width dimensions of the patch grid of each image.
"""
image_outputs = self.vision_tower(pixel_values, grid_thw=image_grid_thw, return_dict=True, **kwargs)
image_features = self.multi_modal_projector(image_outputs.last_hidden_state)
split_sizes = (image_grid_thw.prod(-1) // self.vision_tower.spatial_merge_size**2).tolist()
image_outputs.pooler_output = torch.split(image_features, split_sizes)
return image_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,
):
"""Placeholder masks from `input_ids` or `inputs_embeds`, checked against the feature counts."""
if input_ids is None:
special_image_mask = inputs_embeds == self.get_input_embeddings()(
torch.full((), 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.full((), self.config.video_token_id, dtype=torch.long, device=inputs_embeds.device)
)
special_video_mask = special_video_mask.all(-1)
else:
special_image_mask = input_ids == self.config.image_token_id
special_video_mask = input_ids == self.config.video_token_id
n_image_tokens = special_image_mask.sum()
special_image_mask = special_image_mask.unsqueeze(-1).to(inputs_embeds.device)
if image_features is not None:
torch_compilable_check(
n_image_tokens * inputs_embeds.shape[-1] == 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).to(inputs_embeds.device)
if video_features is not None:
torch_compilable_check(
n_video_tokens * inputs_embeds.shape[-1] == 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
@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,
image_grid_thw: torch.LongTensor | None = None,
pixel_values_videos: torch.FloatTensor | None = None,
video_grid_thw: torch.LongTensor | None = None,
use_cache: bool | None = None,
mm_encoder_outputs: dict[str, BaseModelOutputWithPooling] | None = None,
**kwargs: Unpack[TransformersKwargs],
) -> tuple | Lfm3VlModelOutputWithPast:
r"""
pixel_values (`torch.FloatTensor` of shape `(num_patches, in_channels * temporal_patch_size * patch_size**2)`, *optional*):
The flattened patches of every image, grouped by merge block.
image_grid_thw (`torch.LongTensor` of shape `(num_images, 3)`, *optional*):
The temporal, height and width dimensions of the patch grid of each image.
pixel_values_videos (`torch.FloatTensor` of shape `(num_patches, in_channels * temporal_patch_size * patch_size**2)`, *optional*):
The flattened patches of every video, grouped by merge block.
video_grid_thw (`torch.LongTensor` of shape `(num_videos, 3)`, *optional*):
The temporal, height and width dimensions of the patch grid of each video.
"""
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 or pixel_values_videos is not None) and mm_encoder_outputs is not None:
raise ValueError(
"You cannot specify both pixel_values/pixel_values_videos and mm_encoder_outputs at the same time"
)
if inputs_embeds is None:
inputs_embeds = self.get_input_embeddings()(input_ids)
mm_encoder_outputs = mm_encoder_outputs if mm_encoder_outputs is not None else {}
if (pixel_values is None) != (image_grid_thw is None):
raise ValueError("pixel_values and image_grid_thw must be supplied together")
if (pixel_values_videos is None) != (video_grid_thw is None):
raise ValueError("pixel_values_videos and video_grid_thw must be supplied together")
if mm_encoder_outputs.get("image") is None and pixel_values is not None:
mm_encoder_outputs["image"] = self.get_image_features(
pixel_values, image_grid_thw, return_dict=True, **kwargs
)
if mm_encoder_outputs.get("video") is None and pixel_values_videos is not None:
mm_encoder_outputs["video"] = self.get_video_features(
pixel_values_videos, video_grid_thw, return_dict=True, **kwargs
)
image_features = None
if mm_encoder_outputs.get("image") is not None:
image_features = torch.cat(mm_encoder_outputs["image"].pooler_output, dim=0).to(
inputs_embeds.device, inputs_embeds.dtype
)
image_mask, _ = self.get_placeholder_mask(
input_ids, inputs_embeds=inputs_embeds, image_features=image_features
)
inputs_embeds = inputs_embeds.masked_scatter(image_mask, image_features)
video_features = None
if mm_encoder_outputs.get("video") is not None:
video_features = torch.cat(mm_encoder_outputs["video"].pooler_output, dim=0).to(
inputs_embeds.device, inputs_embeds.dtype
)
_, video_mask = self.get_placeholder_mask(
input_ids, inputs_embeds=inputs_embeds, video_features=video_features
)
inputs_embeds = inputs_embeds.masked_scatter(video_mask, video_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 Lfm3VlModelOutputWithPast(
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,
video_hidden_states=video_features,
)
@accepts_precomputed_kwargs(modality="video")
@can_return_tuple
@auto_docstring(
custom_intro="Obtains video last hidden states from the vision tower and apply multimodal projection."
)
def get_video_features(
self,
pixel_values_videos: torch.FloatTensor,
video_grid_thw: torch.LongTensor,
**kwargs: Unpack[TransformersKwargs],
) -> tuple | BaseModelOutputWithPooling:
r"""
pixel_values_videos (`torch.FloatTensor` of shape `(num_patches, in_channels * temporal_patch_size * patch_size**2)`):
The flattened patches of every video, grouped by merge block.
video_grid_thw (`torch.LongTensor` of shape `(num_videos, 3)`):
The temporal, height and width dimensions of the patch grid of each video.
"""
return self.get_image_features(pixel_values_videos, video_grid_thw, **kwargs)
def reject_packed_generation(config: PreTrainedConfig, input_ids, generation_config, model_kwargs, is_first_iteration):
"""`generate` decodes from the cache its prefill fills, and no mask it builds isolates packed documents
(`position_ids` restarting): the prompt is refused before the prefill, one forward or every chunk, writes to it."""
position_ids, attention_mask = model_kwargs.get("position_ids"), model_kwargs.get("attention_mask")
cache = model_kwargs.get("past_key_values")
if cache is None or position_ids is None or position_ids.ndim != 2:
return
if attention_mask is not None and attention_mask.ndim != 2:
return
has_attention = any(t != "conv" for t in config.get_text_config().layer_types)
filled = bool(cache.get_seq_length() > 0 if has_attention else cache.has_previous_state())
inputs_embeds = model_kwargs.get("inputs_embeds") if is_first_iteration else None
length = input_ids.shape[1] if inputs_embeds is None else inputs_embeds.shape[1]
if generation_config.prefill_chunk_size is not None:
# the chunks run from the start of the prompt
position_ids = position_ids[:, :length]
else:
if filled and has_attention:
# the slice `_prefill` feeds: a prompt that spans the cache is cut to its uncached part
mask_length = getattr(generation_config, "_mask_length", -1)
if attention_mask is not None:
mask_length = attention_mask.shape[1]
if inputs_embeds is not None or mask_length == length:
length -= int(cache.get_seq_length())
position_ids = position_ids[:, position_ids.shape[1] - length :]
if attention_mask is None:
attention_mask = torch.ones_like(position_ids)
if bool(packed_under_padding(position_ids, attention_mask, filled).any()):
raise ValueError(
"`generate` cannot decode packed documents (`position_ids` restarting, or starting at 0 on a cache that "
"holds tokens): attention would span them. Generate each document in its own row of a left-padded batch."
)
def packed_under_padding(position_ids: torch.Tensor, padding_mask: torch.Tensor, filled) -> torch.Tensor:
"""Padding repeats position 0, so packing is a restart between two kept tokens, or a kept position 0 on a filled
cache whose row holds a kept token; attention under a padding mask cannot isolate it."""
restarts = torch.diff(position_ids, prepend=position_ids[:, :1] - 1, dim=-1) != 1
valid = padding_mask[:, -restarts.shape[-1] :].bool()
packed = restarts[:, 1:] & valid[:, 1:] & valid[:, :-1]
if filled:
# a row whose cached tokens are all padding (chunked prefill of a left-padded batch) has no document to pack
# against; a mask covering only the chunk keeps the strict rule
cached = padding_mask[:, : padding_mask.shape[-1] - restarts.shape[-1]]
cached_valid = cached.bool().any(-1, keepdim=True) if cached.shape[-1] else torch.ones_like(valid[:, :1])
packed = packed | (valid[:, :1] & (position_ids[:, :1] == 0) & cached_valid)
return packed
@auto_docstring(
custom_intro="""
The LFM3_VL model which consists of a vision backbone and a language model.
"""
)
class Lfm3VlForConditionalGeneration(Lfm3VlPreTrainedModel, GenerationMixin):
_tied_weights_keys = {"lm_head.weight": "model.language_model.embed_tokens.weight"}
def __init__(self, config: Lfm3VlConfig):
super().__init__(config)
self.model = Lfm3VlModel(config)
self.lm_head = nn.Linear(config.text_config.hidden_size, config.text_config.vocab_size, bias=False)
self.post_init()
def get_output_embeddings(self) -> nn.Module:
return self.lm_head
@auto_docstring
def get_image_features(
self,
pixel_values: torch.FloatTensor,
image_grid_thw: torch.LongTensor,
**kwargs: Unpack[TransformersKwargs],
) -> tuple | BaseModelOutputWithPooling:
r"""
pixel_values (`torch.FloatTensor` of shape `(num_patches, in_channels * temporal_patch_size * patch_size**2)`):
The flattened patches of every image, grouped by merge block.
image_grid_thw (`torch.LongTensor` of shape `(num_images, 3)`):
The temporal, height and width dimensions of the patch grid of each image.
"""
return self.model.get_image_features(pixel_values, 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,
pixel_values: torch.FloatTensor | None = None,
image_grid_thw: torch.LongTensor | None = None,
pixel_values_videos: torch.FloatTensor | None = None,
video_grid_thw: torch.LongTensor | None = None,
labels: torch.LongTensor | None = None,
use_cache: bool | None = None,
logits_to_keep: int | torch.Tensor = 0,
mm_encoder_outputs: dict[str, BaseModelOutputWithPooling] | None = None,
**kwargs: Unpack[TransformersKwargs],
) -> tuple | Lfm3VlCausalLMOutputWithPast:
r"""
pixel_values (`torch.FloatTensor` of shape `(num_patches, in_channels * temporal_patch_size * patch_size**2)`, *optional*):
The flattened patches of every image, grouped by merge block.
image_grid_thw (`torch.LongTensor` of shape `(num_images, 3)`, *optional*):
The temporal, height and width dimensions of the patch grid of each image.
pixel_values_videos (`torch.FloatTensor` of shape `(num_patches, in_channels * temporal_patch_size * patch_size**2)`, *optional*):
The flattened patches of every video, grouped by merge block.
video_grid_thw (`torch.LongTensor` of shape `(num_videos, 3)`, *optional*):
The temporal, height and width dimensions of the patch grid of each video.
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, AutoModelForImageTextToText
>>> model = AutoModelForImageTextToText.from_pretrained("LiquidAI/LFM3-VL-1.9B")
>>> processor = AutoProcessor.from_pretrained("LiquidAI/LFM3-VL-1.9B")
>>> conversation = [
... {
... "role": "user",
... "content": [
... {"type": "image", "url": "https://www.ilankelman.org/stopsigns/australia.jpg"},
... {"type": "text", "text": "What is in this image?"},
... ],
... },
... ]
>>> inputs = processor.apply_chat_template(
... conversation, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt"
... )
>>> outputs = model.generate(**inputs, max_new_tokens=45)
>>> processor.batch_decode(outputs, skip_special_tokens=True)[0]
```"""
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,
image_grid_thw=image_grid_thw,
pixel_values_videos=pixel_values_videos,
video_grid_thw=video_grid_thw,
use_cache=use_cache,
mm_encoder_outputs=mm_encoder_outputs,
**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 Lfm3VlCausalLMOutputWithPast(
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,
video_hidden_states=outputs.video_hidden_states,
)
def _prefill(self, input_ids, generation_config, model_kwargs, is_first_iteration=True):
# the text model's refusal, before the prefill writes to the cache
reject_packed_generation(self.config, input_ids, generation_config, model_kwargs, is_first_iteration)
return super()._prefill(input_ids, generation_config, model_kwargs, is_first_iteration=is_first_iteration)
@auto_docstring
def get_video_features(
self,
pixel_values_videos: torch.FloatTensor,
video_grid_thw: torch.LongTensor,
**kwargs: Unpack[TransformersKwargs],
) -> tuple | BaseModelOutputWithPooling:
r"""
pixel_values_videos (`torch.FloatTensor` of shape `(num_patches, in_channels * temporal_patch_size * patch_size**2)`):
The flattened patches of every video, grouped by merge block.
video_grid_thw (`torch.LongTensor` of shape `(num_videos, 3)`):
The temporal, height and width dimensions of the patch grid of each video.
"""
return self.model.get_video_features(pixel_values_videos, video_grid_thw, **kwargs)
__all__ = ["Lfm3VlForConditionalGeneration", "Lfm3VlModel", "Lfm3VlPreTrainedModel", "Lfm3VlVisionModel"]