saidutta69 commited on
Commit
60b2de5
·
verified ·
1 Parent(s): 668d3ec

Upload Lfm3VlForConditionalGeneration

Browse files
README.md ADDED
@@ -0,0 +1,199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ library_name: transformers
3
+ tags: []
4
+ ---
5
+
6
+ # Model Card for Model ID
7
+
8
+ <!-- Provide a quick summary of what the model is/does. -->
9
+
10
+
11
+
12
+ ## Model Details
13
+
14
+ ### Model Description
15
+
16
+ <!-- Provide a longer summary of what this model is. -->
17
+
18
+ This is the model card of a 🤗 transformers model that has been pushed on the Hub. This model card has been automatically generated.
19
+
20
+ - **Developed by:** [More Information Needed]
21
+ - **Funded by [optional]:** [More Information Needed]
22
+ - **Shared by [optional]:** [More Information Needed]
23
+ - **Model type:** [More Information Needed]
24
+ - **Language(s) (NLP):** [More Information Needed]
25
+ - **License:** [More Information Needed]
26
+ - **Finetuned from model [optional]:** [More Information Needed]
27
+
28
+ ### Model Sources [optional]
29
+
30
+ <!-- Provide the basic links for the model. -->
31
+
32
+ - **Repository:** [More Information Needed]
33
+ - **Paper [optional]:** [More Information Needed]
34
+ - **Demo [optional]:** [More Information Needed]
35
+
36
+ ## Uses
37
+
38
+ <!-- Address questions around how the model is intended to be used, including the foreseeable users of the model and those affected by the model. -->
39
+
40
+ ### Direct Use
41
+
42
+ <!-- This section is for the model use without fine-tuning or plugging into a larger ecosystem/app. -->
43
+
44
+ [More Information Needed]
45
+
46
+ ### Downstream Use [optional]
47
+
48
+ <!-- This section is for the model use when fine-tuned for a task, or when plugged into a larger ecosystem/app -->
49
+
50
+ [More Information Needed]
51
+
52
+ ### Out-of-Scope Use
53
+
54
+ <!-- This section addresses misuse, malicious use, and uses that the model will not work well for. -->
55
+
56
+ [More Information Needed]
57
+
58
+ ## Bias, Risks, and Limitations
59
+
60
+ <!-- This section is meant to convey both technical and sociotechnical limitations. -->
61
+
62
+ [More Information Needed]
63
+
64
+ ### Recommendations
65
+
66
+ <!-- This section is meant to convey recommendations with respect to the bias, risk, and technical limitations. -->
67
+
68
+ Users (both direct and downstream) should be made aware of the risks, biases and limitations of the model. More information needed for further recommendations.
69
+
70
+ ## How to Get Started with the Model
71
+
72
+ Use the code below to get started with the model.
73
+
74
+ [More Information Needed]
75
+
76
+ ## Training Details
77
+
78
+ ### Training Data
79
+
80
+ <!-- This should link to a Dataset Card, perhaps with a short stub of information on what the training data is all about as well as documentation related to data pre-processing or additional filtering. -->
81
+
82
+ [More Information Needed]
83
+
84
+ ### Training Procedure
85
+
86
+ <!-- This relates heavily to the Technical Specifications. Content here should link to that section when it is relevant to the training procedure. -->
87
+
88
+ #### Preprocessing [optional]
89
+
90
+ [More Information Needed]
91
+
92
+
93
+ #### Training Hyperparameters
94
+
95
+ - **Training regime:** [More Information Needed] <!--fp32, fp16 mixed precision, bf16 mixed precision, bf16 non-mixed precision, fp16 non-mixed precision, fp8 mixed precision -->
96
+
97
+ #### Speeds, Sizes, Times [optional]
98
+
99
+ <!-- This section provides information about throughput, start/end time, checkpoint size if relevant, etc. -->
100
+
101
+ [More Information Needed]
102
+
103
+ ## Evaluation
104
+
105
+ <!-- This section describes the evaluation protocols and provides the results. -->
106
+
107
+ ### Testing Data, Factors & Metrics
108
+
109
+ #### Testing Data
110
+
111
+ <!-- This should link to a Dataset Card if possible. -->
112
+
113
+ [More Information Needed]
114
+
115
+ #### Factors
116
+
117
+ <!-- These are the things the evaluation is disaggregating by, e.g., subpopulations or domains. -->
118
+
119
+ [More Information Needed]
120
+
121
+ #### Metrics
122
+
123
+ <!-- These are the evaluation metrics being used, ideally with a description of why. -->
124
+
125
+ [More Information Needed]
126
+
127
+ ### Results
128
+
129
+ [More Information Needed]
130
+
131
+ #### Summary
132
+
133
+
134
+
135
+ ## Model Examination [optional]
136
+
137
+ <!-- Relevant interpretability work for the model goes here -->
138
+
139
+ [More Information Needed]
140
+
141
+ ## Environmental Impact
142
+
143
+ <!-- Total emissions (in grams of CO2eq) and additional considerations, such as electricity usage, go here. Edit the suggested text below accordingly -->
144
+
145
+ Carbon emissions can be estimated using the [Machine Learning Impact calculator](https://mlco2.github.io/impact#compute) presented in [Lacoste et al. (2019)](https://arxiv.org/abs/1910.09700).
146
+
147
+ - **Hardware Type:** [More Information Needed]
148
+ - **Hours used:** [More Information Needed]
149
+ - **Cloud Provider:** [More Information Needed]
150
+ - **Compute Region:** [More Information Needed]
151
+ - **Carbon Emitted:** [More Information Needed]
152
+
153
+ ## Technical Specifications [optional]
154
+
155
+ ### Model Architecture and Objective
156
+
157
+ [More Information Needed]
158
+
159
+ ### Compute Infrastructure
160
+
161
+ [More Information Needed]
162
+
163
+ #### Hardware
164
+
165
+ [More Information Needed]
166
+
167
+ #### Software
168
+
169
+ [More Information Needed]
170
+
171
+ ## Citation [optional]
172
+
173
+ <!-- If there is a paper or blog post introducing the model, the APA and Bibtex information for that should go in this section. -->
174
+
175
+ **BibTeX:**
176
+
177
+ [More Information Needed]
178
+
179
+ **APA:**
180
+
181
+ [More Information Needed]
182
+
183
+ ## Glossary [optional]
184
+
185
+ <!-- If relevant, include terms and calculations in this section that can help readers understand the model or model card. -->
186
+
187
+ [More Information Needed]
188
+
189
+ ## More Information [optional]
190
+
191
+ [More Information Needed]
192
+
193
+ ## Model Card Authors [optional]
194
+
195
+ [More Information Needed]
196
+
197
+ ## Model Card Contact
198
+
199
+ [More Information Needed]
config.json ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "Lfm3VlForConditionalGeneration"
4
+ ],
5
+ "auto_map": {
6
+ "AutoConfig": "configuration_lfm3_vl.Lfm3VlConfig",
7
+ "AutoModel": "modeling_lfm3_vl.Lfm3VlModel",
8
+ "AutoModelForImageTextToText": "modeling_lfm3_vl.Lfm3VlForConditionalGeneration"
9
+ },
10
+ "bos_token_id": 124894,
11
+ "downsample_factor": 2,
12
+ "dtype": "bfloat16",
13
+ "eos_token_id": 124900,
14
+ "image_token_id": 125030,
15
+ "model_type": "lfm3_vl",
16
+ "pad_token_id": 124893,
17
+ "projector_bias": true,
18
+ "projector_hidden_act": "gelu",
19
+ "projector_hidden_size": 4096,
20
+ "projector_layer_norm_eps": 1e-06,
21
+ "text_config": {
22
+ "_name_or_path": "",
23
+ "architectures": null,
24
+ "bos_token_id": 124894,
25
+ "chunk_size_feed_forward": 0,
26
+ "conv_L_cache": 7,
27
+ "conv_bias": false,
28
+ "dtype": "bfloat16",
29
+ "eos_token_id": 124900,
30
+ "head_dim": 128,
31
+ "hidden_size": 2048,
32
+ "id2label": {
33
+ "0": "LABEL_0",
34
+ "1": "LABEL_1"
35
+ },
36
+ "initializer_range": 0.02,
37
+ "intermediate_size": 7680,
38
+ "is_encoder_decoder": false,
39
+ "label2id": {
40
+ "LABEL_0": 0,
41
+ "LABEL_1": 1
42
+ },
43
+ "layer_types": [
44
+ "conv",
45
+ "conv",
46
+ "full_attention",
47
+ "conv",
48
+ "sliding_attention",
49
+ "conv",
50
+ "full_attention",
51
+ "conv",
52
+ "sliding_attention",
53
+ "conv",
54
+ "full_attention",
55
+ "conv",
56
+ "sliding_attention",
57
+ "conv",
58
+ "full_attention",
59
+ "conv",
60
+ "sliding_attention",
61
+ "conv",
62
+ "full_attention",
63
+ "conv",
64
+ "sliding_attention",
65
+ "conv",
66
+ "full_attention",
67
+ "conv"
68
+ ],
69
+ "max_position_embeddings": 128000,
70
+ "model_type": "lfm3",
71
+ "norm_eps": 1e-05,
72
+ "num_attention_heads": 16,
73
+ "num_hidden_layers": 24,
74
+ "num_key_value_heads": 4,
75
+ "output_attentions": false,
76
+ "output_hidden_states": false,
77
+ "pad_token_id": 124893,
78
+ "problem_type": null,
79
+ "return_dict": true,
80
+ "rope_parameters": {
81
+ "full_attention": {
82
+ "partial_rotary_factor": 0.5,
83
+ "rope_theta": 1000000.0,
84
+ "rope_type": "default"
85
+ },
86
+ "sliding_attention": {
87
+ "rope_theta": 10000,
88
+ "rope_type": "default"
89
+ }
90
+ },
91
+ "sliding_window": 256,
92
+ "tie_word_embeddings": true,
93
+ "use_cache": true,
94
+ "use_sandwich_norm": true,
95
+ "vocab_size": 128000
96
+ },
97
+ "tie_word_embeddings": true,
98
+ "transformers_version": "5.21.0",
99
+ "video_token_id": 125029,
100
+ "vision_config": {
101
+ "_name_or_path": "",
102
+ "architectures": null,
103
+ "chunk_size_feed_forward": 0,
104
+ "conv_kernel_size": 3,
105
+ "dtype": "bfloat16",
106
+ "hidden_size": 1024,
107
+ "id2label": {
108
+ "0": "LABEL_0",
109
+ "1": "LABEL_1"
110
+ },
111
+ "in_channels": 3,
112
+ "initializer_range": 0.02,
113
+ "intermediate_size": 2560,
114
+ "is_encoder_decoder": false,
115
+ "label2id": {
116
+ "LABEL_0": 0,
117
+ "LABEL_1": 1
118
+ },
119
+ "layer_types": [
120
+ "conv",
121
+ "conv",
122
+ "full_attention",
123
+ "conv",
124
+ "full_attention",
125
+ "conv",
126
+ "full_attention",
127
+ "conv",
128
+ "full_attention",
129
+ "conv",
130
+ "full_attention",
131
+ "conv",
132
+ "full_attention",
133
+ "conv"
134
+ ],
135
+ "model_type": "lfm3_vl_vision",
136
+ "norm_eps": 1e-05,
137
+ "num_attention_heads": 16,
138
+ "num_hidden_layers": 14,
139
+ "num_key_value_heads": 8,
140
+ "output_attentions": false,
141
+ "output_hidden_states": false,
142
+ "patch_size": 16,
143
+ "problem_type": null,
144
+ "return_dict": true,
145
+ "rope_parameters": {
146
+ "rope_theta": 1000000.0,
147
+ "rope_type": "axial"
148
+ },
149
+ "spatial_merge_size": 2,
150
+ "temporal_patch_size": 2
151
+ },
152
+ "vision_end_token_id": 125010,
153
+ "vision_start_token_id": 125009
154
+ }
configuration_lfm3.py ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2026 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ from huggingface_hub.dataclasses import strict
15
+
16
+ from transformers.configuration_utils import PreTrainedConfig
17
+ from transformers.utils import auto_docstring
18
+
19
+
20
+ @auto_docstring(checkpoint="LiquidAI/LFM3-1.7B")
21
+ @strict
22
+ class Lfm3Config(PreTrainedConfig):
23
+ r"""
24
+ rope_parameters (`dict[str, RopeParameters]`, *optional*):
25
+ RoPE parameters keyed by attention layer type, for example `{"full_attention": {"rope_type": "default",
26
+ "rope_theta": 1000000.0, "partial_rotary_factor": 0.5}, "sliding_attention": {"rope_type": "default",
27
+ "rope_theta": 10000.0}}`. `conv` layers do not use RoPE and have no entry. Full-attention layers rotate the
28
+ first `partial_rotary_factor * head_dim` dimensions and leave the rest unrotated.
29
+ conv_bias (`bool`, *optional*, defaults to `False`):
30
+ Whether to use bias in the conv layers.
31
+ conv_L_cache (`int`, *optional*, defaults to 7):
32
+ L_cache dim in the conv layers.
33
+ layer_types (`list[str]`, *optional*):
34
+ Per-layer operator type, each one of `"full_attention"`, `"sliding_attention"` or `"conv"`. Defaults to the
35
+ LFM3 pattern of a full-attention layer every fourth layer starting from the third, a sliding-attention layer
36
+ in between, and gated short convolutions elsewhere.
37
+ sliding_window (`int`, *optional*, defaults to 256):
38
+ Sliding-window size of the `sliding_attention` layers.
39
+ use_sandwich_norm (`bool`, *optional*, defaults to `True`):
40
+ Whether to apply an RMSNorm to the operator and feed-forward outputs before they enter the residual stream.
41
+
42
+ ```python
43
+ >>> from transformers import Lfm3Model, Lfm3Config
44
+
45
+ >>> # Initializing a LFM3 model
46
+ >>> configuration = Lfm3Config()
47
+
48
+ >>> # Initializing a model from the LFM3-1.7B style configuration
49
+ >>> model = Lfm3Model(configuration)
50
+
51
+ >>> # Accessing the model configuration
52
+ >>> configuration = model.config
53
+ ```
54
+ """
55
+
56
+ model_type = "lfm3"
57
+ keys_to_ignore_at_inference = ["past_key_values"]
58
+ default_theta = 1000000.0
59
+
60
+ vocab_size: int = 128_000
61
+ hidden_size: int = 2048
62
+ intermediate_size: int = 7680
63
+ num_hidden_layers: int = 24
64
+ num_attention_heads: int = 16
65
+ num_key_value_heads: int = 4
66
+ max_position_embeddings: int = 128_000
67
+ initializer_range: float = 0.02
68
+ norm_eps: float = 0.00001
69
+ use_cache: bool = True
70
+ pad_token_id: int | None = 124_893
71
+ bos_token_id: int | None = 124_894
72
+ eos_token_id: int | list[int] | None = 124_900
73
+ tie_word_embeddings: bool = True
74
+ rope_parameters: dict | None = None
75
+ conv_bias: bool = False
76
+ conv_L_cache: int = 7
77
+ layer_types: list[str] | None = None
78
+ _attention_layer_types = ("full_attention", "sliding_attention")
79
+ head_dim: int | None = None
80
+ sliding_window: int | None = 256
81
+ use_sandwich_norm: bool = True
82
+
83
+ def __post_init__(self, **kwargs):
84
+ if self.head_dim is None:
85
+ self.head_dim = self.hidden_size // self.num_attention_heads
86
+ if self.rope_parameters is None:
87
+ self.rope_parameters = {
88
+ "full_attention": {"rope_type": "default", "rope_theta": 1_000_000.0, "partial_rotary_factor": 0.5},
89
+ "sliding_attention": {"rope_type": "default", "rope_theta": 10_000.0},
90
+ }
91
+ if self.layer_types is None:
92
+ self.layer_types = [
93
+ "full_attention" if i % 4 == 2 else "sliding_attention" if i % 4 == 0 and i > 0 else "conv"
94
+ for i in range(self.num_hidden_layers)
95
+ ]
96
+
97
+ self.tie_word_embeddings = kwargs.pop("tie_embedding", self.tie_word_embeddings)
98
+ super().__post_init__(**kwargs)
99
+
100
+ def validate_architecture(self):
101
+ """Part of ``@strict``-powered validation."""
102
+ if len(self.layer_types) != self.num_hidden_layers:
103
+ raise ValueError(
104
+ f"`layer_types` has {len(self.layer_types)} entries but `num_hidden_layers` is {self.num_hidden_layers}."
105
+ )
106
+ allowed_layer_types = (*self._attention_layer_types, "conv")
107
+ invalid_layer_types = [layer_type for layer_type in self.layer_types if layer_type not in allowed_layer_types]
108
+ if invalid_layer_types:
109
+ raise ValueError(f"`layer_types` entries must be one of {allowed_layer_types}; got {invalid_layer_types}.")
110
+ if "sliding_attention" in self.layer_types and self.sliding_window is None:
111
+ raise ValueError("`layer_types` has `sliding_attention` layers but `sliding_window` is None.")
112
+ missing_rope = [
113
+ layer_type
114
+ for layer_type in self._attention_layer_types
115
+ if layer_type in self.layer_types and layer_type not in self.rope_parameters
116
+ ]
117
+ if missing_rope:
118
+ raise ValueError(f"`rope_parameters` has no entry for the {missing_rope} layers in `layer_types`.")
119
+ for layer_type, rope_params in self.rope_parameters.items():
120
+ if not isinstance(rope_params, dict):
121
+ continue
122
+ if (
123
+ rope_params.get("rope_type", "default") in ("dynamic", "longrope")
124
+ and rope_params.get("partial_rotary_factor", 1.0) < 1
125
+ ):
126
+ raise ValueError(
127
+ f"`{rope_params['rope_type']}` RoPE recomputes its frequencies during the forward pass over the "
128
+ f"rotated dimension only and cannot keep LFM3's partial rotation on `{layer_type}` layers."
129
+ )
130
+
131
+
132
+ __all__ = ["Lfm3Config"]
configuration_lfm3_vl.py ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2026 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ from huggingface_hub.dataclasses import strict
15
+
16
+ from transformers.configuration_utils import PreTrainedConfig
17
+ from transformers.models.auto import CONFIG_MAPPING, AutoConfig
18
+ from transformers.utils import auto_docstring
19
+
20
+ from .configuration_lfm3 import Lfm3Config
21
+
22
+
23
+ def _config_class(model_type):
24
+ # the LFM3 configs ship with the checkpoint instead of the transformers registry
25
+ remote = {"lfm3": Lfm3Config, "lfm3_vl_vision": Lfm3VlVisionConfig}
26
+ return remote.get(model_type) or CONFIG_MAPPING[model_type]
27
+
28
+
29
+ @auto_docstring(checkpoint="LiquidAI/LFM3-VL-2B")
30
+ @strict
31
+ class Lfm3VlVisionConfig(PreTrainedConfig):
32
+ r"""
33
+ layer_types (`list[str]`, *optional*):
34
+ Per-layer operator type, each one of `"full_attention"` or `"conv"`. Defaults to two gated convolution
35
+ layers followed by alternating attention and convolution layers.
36
+ patch_size (`int`, *optional*, defaults to 16):
37
+ The spatial size of each image patch.
38
+ temporal_patch_size (`int`, *optional*, defaults to 2):
39
+ The number of video frames packed into one patch.
40
+ spatial_merge_size (`int`, *optional*, defaults to 2):
41
+ The side of the square block of patches merged into one language token.
42
+ conv_kernel_size (`int`, *optional*, defaults to 3):
43
+ The kernel size of the depthwise 2D convolution layers.
44
+ norm_eps (`float`, *optional*, defaults to 1e-05):
45
+ The epsilon used by the RMS normalization layers.
46
+ """
47
+
48
+ model_type = "lfm3_vl_vision"
49
+ base_config_key = "vision_config"
50
+ default_theta = 1_000_000.0
51
+ default_rope_type = "axial"
52
+ _attention_layer_types = ("full_attention",)
53
+
54
+ hidden_size: int = 1024
55
+ intermediate_size: int = 2560
56
+ num_hidden_layers: int = 14
57
+ num_attention_heads: int = 16
58
+ num_key_value_heads: int = 8
59
+ layer_types: list[str] | None = None
60
+ patch_size: int = 16
61
+ temporal_patch_size: int = 2
62
+ in_channels: int = 3
63
+ spatial_merge_size: int = 2
64
+ conv_kernel_size: int = 3
65
+ norm_eps: float = 1e-5
66
+ initializer_range: float = 0.02
67
+ rope_parameters: dict | None = None
68
+
69
+ def __post_init__(self, **kwargs):
70
+ if self.layer_types is None:
71
+ self.layer_types = ["conv" if i < 2 or i % 2 else "full_attention" for i in range(self.num_hidden_layers)]
72
+ super().__post_init__(**kwargs)
73
+
74
+ def validate_architecture(self):
75
+ """Part of ``@strict``-powered validation."""
76
+ if len(self.layer_types) != self.num_hidden_layers:
77
+ raise ValueError(
78
+ f"`layer_types` has {len(self.layer_types)} entries but `num_hidden_layers` is {self.num_hidden_layers}."
79
+ )
80
+ allowed_layer_types = (*self._attention_layer_types, "conv")
81
+ invalid_layer_types = [layer_type for layer_type in self.layer_types if layer_type not in allowed_layer_types]
82
+ if invalid_layer_types:
83
+ raise ValueError(f"`layer_types` entries must be one of {allowed_layer_types}; got {invalid_layer_types}.")
84
+ if self.hidden_size % self.num_attention_heads:
85
+ raise ValueError("`hidden_size` must be a multiple of `num_attention_heads`.")
86
+
87
+
88
+ # The first checkpoints spell every text and `vision_*` field at the top level of the config
89
+ _LEGACY_TEXT_KEYS = (
90
+ "vocab_size",
91
+ "hidden_size",
92
+ "intermediate_size",
93
+ "num_hidden_layers",
94
+ "num_attention_heads",
95
+ "num_key_value_heads",
96
+ "head_dim",
97
+ "layer_types",
98
+ "conv_bias",
99
+ "conv_L_cache",
100
+ "sliding_window",
101
+ "max_position_embeddings",
102
+ "use_sandwich_norm",
103
+ "use_cache",
104
+ )
105
+ _LEGACY_VISION_KEYS = {
106
+ "vision_hidden_size": "hidden_size",
107
+ "vision_intermediate_size": "intermediate_size",
108
+ "vision_num_hidden_layers": "num_hidden_layers",
109
+ "vision_num_attention_heads": "num_attention_heads",
110
+ "vision_num_key_value_heads": "num_key_value_heads",
111
+ "vision_layer_types": "layer_types",
112
+ "vision_patch_size": "patch_size",
113
+ "vision_temporal_patch_size": "temporal_patch_size",
114
+ "vision_num_channels": "in_channels",
115
+ "vision_spatial_merge_size": "spatial_merge_size",
116
+ }
117
+ _LEGACY_DROPPED_KEYS = ("vision_patch_dim", "vision_max_position_embeddings")
118
+
119
+
120
+ @auto_docstring(checkpoint="LiquidAI/LFM3-VL-2B")
121
+ @strict
122
+ class Lfm3VlConfig(PreTrainedConfig):
123
+ r"""
124
+ video_token_id (`int`, *optional*, defaults to 125029):
125
+ The id of the placeholder token for video patches.
126
+ vision_start_token_id (`int`, *optional*, defaults to 125009):
127
+ The id of the token opening an image or a video frame.
128
+ vision_end_token_id (`int`, *optional*, defaults to 125010):
129
+ The id of the token closing an image or a video frame.
130
+ projector_layer_norm_eps (`float`, *optional*, defaults to 1e-06):
131
+ The epsilon of the layer normalization applied to the vision features before projection.
132
+ downsample_factor (`int`, *optional*, defaults to 2):
133
+ The side of the square block of vision tokens merged by the projector, equal to the vision encoder's
134
+ `spatial_merge_size`.
135
+ """
136
+
137
+ model_type = "lfm3_vl"
138
+ sub_configs = {"text_config": AutoConfig, "vision_config": Lfm3VlVisionConfig}
139
+
140
+ text_config: dict | PreTrainedConfig | None = None
141
+ vision_config: dict | PreTrainedConfig | None = None
142
+ image_token_id: int = 125_030
143
+ video_token_id: int = 125_029
144
+ vision_start_token_id: int = 125_009
145
+ vision_end_token_id: int = 125_010
146
+ projector_hidden_act: str = "gelu"
147
+ projector_hidden_size: int = 4096
148
+ projector_bias: bool = True
149
+ projector_layer_norm_eps: float = 1e-6
150
+ downsample_factor: int = 2
151
+ tie_word_embeddings: bool = True
152
+
153
+ def __post_init__(self, **kwargs):
154
+ if "vision_hidden_size" in kwargs:
155
+ kwargs = self._convert_legacy_flat_config(kwargs)
156
+
157
+ if isinstance(self.vision_config, dict):
158
+ self.vision_config = self.sub_configs["vision_config"](**self.vision_config)
159
+ elif self.vision_config is None:
160
+ self.vision_config = self.sub_configs["vision_config"]()
161
+
162
+ if isinstance(self.text_config, dict):
163
+ self.text_config = {"model_type": "lfm3", **self.text_config}
164
+ self.text_config = _config_class(self.text_config["model_type"])(**self.text_config)
165
+ elif self.text_config is None:
166
+ self.text_config = _config_class("lfm3")()
167
+ self.validate_vision_fields()
168
+
169
+ super().__post_init__(**kwargs)
170
+
171
+ def validate_vision_fields(self):
172
+ """Part of `@strict`-powered validation, so `validate()` and `save_pretrained` refuse later edits too."""
173
+ if self.downsample_factor != self.vision_config.spatial_merge_size:
174
+ raise ValueError("downsample_factor must match vision_config.spatial_merge_size")
175
+ # an `inputs_embeds` forward embeds both placeholder ids to find them
176
+ vocab_size = self.text_config.vocab_size
177
+ for name in ("image_token_id", "video_token_id"):
178
+ if not 0 <= getattr(self, name) < vocab_size:
179
+ raise ValueError(f"{name}={getattr(self, name)} must lie in [0, text_config.vocab_size={vocab_size})")
180
+
181
+ def _convert_legacy_flat_config(self, kwargs: dict) -> dict:
182
+ text_config = {key: kwargs.pop(key) for key in _LEGACY_TEXT_KEYS if key in kwargs}
183
+ text_config["norm_eps"] = kwargs.pop("rms_norm_eps", 1e-5)
184
+ text_config["rope_parameters"] = {
185
+ "full_attention": {
186
+ "rope_type": "default",
187
+ "rope_theta": kwargs.pop("full_attention_rope_theta", 1_000_000.0),
188
+ "partial_rotary_factor": kwargs.pop("partial_rotary_factor", 0.5),
189
+ },
190
+ "sliding_attention": {
191
+ "rope_type": "default",
192
+ "rope_theta": kwargs.pop("sliding_attention_rope_theta", 10_000.0),
193
+ },
194
+ }
195
+ for key in ("pad_token_id", "bos_token_id", "eos_token_id"):
196
+ if kwargs.get(key) is not None:
197
+ text_config[key] = kwargs[key]
198
+ text_config["tie_word_embeddings"] = self.tie_word_embeddings
199
+
200
+ vision_config = {new: kwargs.pop(old) for old, new in _LEGACY_VISION_KEYS.items() if old in kwargs}
201
+ vision_config["norm_eps"] = text_config["norm_eps"]
202
+ vision_config["rope_parameters"] = {"rope_type": "axial", "rope_theta": kwargs.pop("vision_rope_theta", 1e6)}
203
+ for key in _LEGACY_DROPPED_KEYS:
204
+ kwargs.pop(key, None)
205
+
206
+ self.text_config = text_config
207
+ self.vision_config = vision_config
208
+ return kwargs
209
+
210
+
211
+ __all__ = ["Lfm3VlConfig", "Lfm3VlVisionConfig"]
generation_config.json ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_from_model_config": true,
3
+ "bos_token_id": 124894,
4
+ "do_sample": true,
5
+ "eos_token_id": 124900,
6
+ "output_attentions": false,
7
+ "output_hidden_states": false,
8
+ "pad_token_id": 124893,
9
+ "repetition_penalty": 1.1,
10
+ "temperature": 0.1,
11
+ "top_k": 50,
12
+ "transformers_version": "5.21.0",
13
+ "use_cache": true
14
+ }
lfm3_conv.py ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2026 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """Inference kernels for LFM3's width-7 depthwise convolution."""
15
+
16
+ import torch
17
+
18
+ from transformers.utils import is_triton_available
19
+
20
+
21
+ if is_triton_available():
22
+ import triton
23
+ import triton.language as tl
24
+
25
+ # the length and strides are runtime arguments, or every prompt length would compile its own kernel
26
+ @triton.jit(do_not_specialize=["T", "X0", "X1", "X2", "S0", "S1", "ST"])
27
+ def _prefill(
28
+ X,
29
+ W,
30
+ BIAS,
31
+ SEQ,
32
+ Y,
33
+ T,
34
+ D: tl.constexpr,
35
+ X0,
36
+ X1,
37
+ X2,
38
+ S0,
39
+ S1,
40
+ ST,
41
+ HAS_BIAS: tl.constexpr,
42
+ PACKED: tl.constexpr,
43
+ BLOCK: tl.constexpr,
44
+ ):
45
+ # int64 offsets: batch * channels * length can exceed 2**31 elements
46
+ b, d = tl.program_id(0).to(tl.int64), tl.program_id(1).to(tl.int64)
47
+ t = tl.program_id(2).to(tl.int64) * BLOCK + tl.arange(0, BLOCK)
48
+ acc = tl.full((BLOCK,), 0, tl.float32)
49
+ if PACKED:
50
+ doc = tl.load(SEQ + b * S0 + (t - T + ST) * S1, (t < T) & (t >= T - ST), other=-1)
51
+ for k in tl.static_range(7):
52
+ pos = t + k - 6
53
+ valid = (t < T) & (pos >= 0)
54
+ if PACKED:
55
+ prev = tl.load(SEQ + b * S0 + (pos - T + ST) * S1, valid & (pos >= T - ST), other=-1)
56
+ valid = valid & (doc == prev)
57
+ x = tl.load(X + b * X0 + d * X1 + pos * X2, valid, other=0).to(tl.float32)
58
+ w = tl.load(W + d * 7 + k).to(tl.float32)
59
+ acc += x * w
60
+ if HAS_BIAS:
61
+ acc += tl.load(BIAS + d).to(tl.float32)
62
+ tl.store(Y + (b * D + d) * T + t, acc, t < T)
63
+
64
+ @triton.jit
65
+ def _decode(
66
+ X,
67
+ W,
68
+ BIAS,
69
+ STATE,
70
+ Y,
71
+ D: tl.constexpr,
72
+ K: tl.constexpr,
73
+ X0: tl.constexpr,
74
+ X1: tl.constexpr,
75
+ S0: tl.constexpr,
76
+ S1: tl.constexpr,
77
+ S2: tl.constexpr,
78
+ HAS_BIAS: tl.constexpr,
79
+ BLOCK: tl.constexpr,
80
+ ):
81
+ b = tl.program_id(0).to(tl.int64)
82
+ d = tl.program_id(1).to(tl.int64) * BLOCK + tl.arange(0, BLOCK)
83
+ k = tl.arange(0, 8)
84
+ # Load the complete state before writing the shifted window.
85
+ old = tl.load(STATE + b * S0 + d[:, None] * S1 + k[None, :] * S2, (d[:, None] < D) & (k[None, :] < K), other=0)
86
+ x = tl.load(X + b * X0 + d * X1, d < D, other=0)
87
+ shifted = tl.gather(old, tl.broadcast_to(tl.minimum(k + 1, 7)[None, :], (BLOCK, 8)), 1)
88
+ shifted = tl.where(k[None, :] == K - 1, x[:, None], shifted)
89
+ # A cache may store either K=6 previous inputs or K=7 including the oldest discarded input.
90
+ positions = k + K - 6
91
+ values = tl.gather(old, tl.broadcast_to(tl.minimum(positions, 7)[None, :], (BLOCK, 8)), 1)
92
+ values = tl.where(k[None, :] == 6, x[:, None], values)
93
+ weights = tl.load(W + d[:, None] * 7 + k[None, :], (d[:, None] < D) & (k[None, :] < 7), other=0)
94
+ acc = tl.sum(values.to(tl.float32) * weights.to(tl.float32), 1)
95
+ if HAS_BIAS:
96
+ acc += tl.load(BIAS + d, d < D, other=0).to(tl.float32)
97
+ tl.store(STATE + b * S0 + d[:, None] * S1 + k[None, :] * S2, shifted, (d[:, None] < D) & (k[None, :] < K))
98
+ tl.store(Y + b * D + d, acc, d < D)
99
+
100
+
101
+ def can_use_lfm3_conv(hidden_states, weight):
102
+ return (
103
+ is_triton_available()
104
+ and hasattr(tl, "gather") # the decode kernel needs Triton >= 3.3
105
+ and hidden_states.is_cuda
106
+ and not torch.is_grad_enabled()
107
+ and hidden_states.dtype == weight.dtype
108
+ and hidden_states.dtype in (torch.float16, torch.bfloat16, torch.float32)
109
+ and weight.shape[-1] == 7
110
+ )
111
+
112
+
113
+ def lfm3_causal_conv1d(hidden_states, weight, bias=None, seq_idx=None):
114
+ batch, channels, length = hidden_states.shape
115
+ # the kernels index every tensor by the batch and channel of `hidden_states` and cannot bound the others
116
+ if weight.shape != (channels, 7) or (bias is not None and bias.shape != (channels,)):
117
+ raise ValueError(
118
+ f"`weight` must have shape ({channels}, 7) and `bias` ({channels},), got {tuple(weight.shape)}"
119
+ )
120
+ if seq_idx is not None and (
121
+ seq_idx.ndim != 2 or seq_idx.shape[0] not in (1, batch) or seq_idx.dtype not in (torch.int32, torch.int64)
122
+ ):
123
+ raise ValueError(
124
+ f"`seq_idx` must be an int32 or int64 tensor of shape (1, length) or ({batch}, length), got "
125
+ f"{seq_idx.dtype} {tuple(seq_idx.shape)}"
126
+ )
127
+ if any(t is not None and t.device != hidden_states.device for t in (weight, bias, seq_idx)):
128
+ raise ValueError(
129
+ f"`weight`, `bias` and `seq_idx` must be on the device of `hidden_states` ({hidden_states.device})"
130
+ )
131
+ output = torch.empty_like(hidden_states, memory_format=torch.contiguous_format)
132
+ # Triton launches on the current device; the model may live on another one (`device_map`, `.to("cuda:1")`).
133
+ with torch.cuda.device(hidden_states.device):
134
+ _prefill[(batch, channels, triton.cdiv(length, 128))](
135
+ hidden_states,
136
+ # the kernels index `bias` and `weight` densely
137
+ weight.contiguous(),
138
+ bias.contiguous() if bias is not None else None,
139
+ seq_idx,
140
+ output,
141
+ length,
142
+ channels,
143
+ *hidden_states.stride(),
144
+ *(
145
+ (0 if seq_idx.shape[0] == 1 else seq_idx.stride(0), seq_idx.stride(1))
146
+ if seq_idx is not None
147
+ else (0, 0)
148
+ ),
149
+ seq_idx.shape[-1] if seq_idx is not None else 0,
150
+ bias is not None,
151
+ seq_idx is not None,
152
+ 128,
153
+ enable_fp_fusion=False,
154
+ )
155
+ return output
156
+
157
+
158
+ def lfm3_causal_conv1d_update(hidden_states, conv_state, weight, bias=None):
159
+ batch, channels, length = hidden_states.shape
160
+ # the kernel addresses one position per row and bounds nothing by the other tensors' shapes
161
+ if length != 1:
162
+ raise ValueError(f"`hidden_states` must hold one position per row, got {tuple(hidden_states.shape)}")
163
+ # the 8-wide tile holds a 6- or 7-wide state (see `positions` in `_decode`)
164
+ if (
165
+ conv_state.ndim != 3
166
+ or conv_state.shape[:2] != (batch, channels)
167
+ or conv_state.shape[-1] not in (6, 7)
168
+ or weight.shape != (channels, 7)
169
+ ):
170
+ raise ValueError(
171
+ f"`conv_state` must have shape ({batch}, {channels}, 6 or 7) and `weight` ({channels}, 7), got "
172
+ f"{tuple(conv_state.shape)} and {tuple(weight.shape)}"
173
+ )
174
+ if bias is not None and bias.shape != (channels,):
175
+ raise ValueError(f"`bias` must have shape ({channels},), got {tuple(bias.shape)}")
176
+ # the kernel writes the state in place: rows or channels sharing memory (an `expand`ed cache) would race
177
+ extent = 0
178
+ for stride, size in sorted(zip(conv_state.stride(), conv_state.shape)):
179
+ if size > 1 and stride <= extent:
180
+ raise ValueError(f"`conv_state` must not overlap itself, got strides {conv_state.stride()}")
181
+ extent += stride * (size - 1)
182
+ if any(t is not None and t.device != hidden_states.device for t in (conv_state, weight, bias)):
183
+ raise ValueError(
184
+ f"`conv_state`, `weight` and `bias` must be on the device of `hidden_states` ({hidden_states.device})"
185
+ )
186
+ output = torch.empty_like(hidden_states, memory_format=torch.contiguous_format)
187
+ with torch.cuda.device(hidden_states.device):
188
+ _decode[(batch, triton.cdiv(channels, 32))](
189
+ hidden_states,
190
+ weight.contiguous(),
191
+ bias.contiguous() if bias is not None else None,
192
+ conv_state,
193
+ output,
194
+ channels,
195
+ conv_state.shape[-1],
196
+ *hidden_states.stride()[:2],
197
+ *conv_state.stride(),
198
+ bias is not None,
199
+ 32,
200
+ enable_fp_fusion=False,
201
+ )
202
+ return output
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:91d527edb09f1e382172c9bf4028247676f92b6c1545f1e4d6b8920b4eba906a
3
+ size 3836417920
modeling_lfm3.py ADDED
@@ -0,0 +1,881 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2026 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import copy
15
+ from collections.abc import Callable
16
+
17
+ import torch
18
+ import torch.nn.functional as F
19
+ from torch import nn
20
+
21
+ from transformers import initialization as init
22
+ from transformers.activations import ACT2FN
23
+ from transformers.cache_utils import Cache, DynamicCache
24
+ from transformers.configuration_utils import PreTrainedConfig
25
+ from transformers.generation import GenerationMixin
26
+ from transformers.integrations import (
27
+ use_kernel_forward_from_hub,
28
+ use_kernel_func_from_hub_with_fallback,
29
+ use_kernelized_func,
30
+ )
31
+ from transformers.integrations.accelerate import force_accelerate_hooks
32
+ from transformers.masking_utils import (
33
+ create_causal_mask,
34
+ create_recurrent_attention_mask,
35
+ create_sliding_window_causal_mask,
36
+ )
37
+ from transformers.modeling_layers import GradientCheckpointingLayer
38
+ from transformers.modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast
39
+ from transformers.modeling_rope_utils import ROPE_INIT_FUNCTIONS, dynamic_rope_update
40
+ from transformers.modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
41
+ from transformers.processing_utils import Unpack
42
+ from transformers.utils import TransformersKwargs, auto_docstring, can_return_tuple, torch_compilable_check
43
+ from transformers.utils.generic import merge_with_config_defaults
44
+ from transformers.utils.import_utils import is_tracing
45
+ from transformers.utils.output_capturing import capture_outputs
46
+
47
+ from .configuration_lfm3 import Lfm3Config
48
+ from .lfm3_conv import can_use_lfm3_conv, lfm3_causal_conv1d, lfm3_causal_conv1d_update
49
+
50
+
51
+ @use_kernel_forward_from_hub("RMSNorm")
52
+ class Lfm3RMSNorm(nn.Module):
53
+ def __init__(self, hidden_size, eps: float = 1e-6) -> None:
54
+ """
55
+ Lfm3RMSNorm is equivalent to T5LayerNorm
56
+ """
57
+ super().__init__()
58
+ self.weight = nn.Parameter(torch.ones(hidden_size))
59
+ self.variance_epsilon = eps
60
+
61
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
62
+ input_dtype = hidden_states.dtype
63
+ hidden_states = hidden_states.to(torch.float32)
64
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
65
+ hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
66
+ return self.weight * hidden_states.to(input_dtype)
67
+
68
+ def extra_repr(self):
69
+ return f"{tuple(self.weight.shape)}, eps={self.variance_epsilon}"
70
+
71
+
72
+ class Lfm3RotaryEmbedding(nn.Module):
73
+ """
74
+ Rotary embedding per attention layer type, like Gemma 3; `conv` layers register no buffers. Partial RoPE keeps the
75
+ frequency progression of the full head dimension and zeroes the tail instead of shrinking the rotated dimension.
76
+ """
77
+
78
+ def __init__(self, config: Lfm3Config, device=None):
79
+ super().__init__()
80
+ self.max_seq_len_cached = config.max_position_embeddings
81
+ self.original_max_seq_len = config.max_position_embeddings
82
+ self.config = config
83
+ self.layer_types = sorted(set(config.layer_types) & set(config.rope_parameters))
84
+ self.rope_type = {}
85
+ for layer_type in self.layer_types:
86
+ rope_params = self.config.rope_parameters[layer_type]
87
+ self.rope_type[layer_type] = rope_params["rope_type"]
88
+ curr_inv_freq, curr_attention_scaling = self.compute_rope_parameters(config, device, layer_type=layer_type)
89
+ setattr(self, f"{layer_type}_inv_freq", nn.Buffer(curr_inv_freq, persistent=False))
90
+ setattr(self, f"{layer_type}_original_inv_freq", nn.Buffer(curr_inv_freq.clone(), persistent=False))
91
+ setattr(self, f"{layer_type}_attention_scaling", curr_attention_scaling)
92
+
93
+ @staticmethod
94
+ def compute_default_rope_parameters(
95
+ config: Lfm3Config, device=None, layer_type: str | None = None, **kwargs
96
+ ) -> tuple[torch.Tensor, float]:
97
+ """
98
+ Inverse frequencies of the default RoPE with LFM3's zero-tail partial rotation: the first
99
+ `partial_rotary_factor * head_dim // 2` frequencies follow the progression of the full head dimension and the
100
+ rest are zero, leaving those dimensions unrotated.
101
+
102
+ Returns:
103
+ Tuple of (`torch.Tensor`, `float`): the inverse frequencies and the attention scaling factor (always 1).
104
+ """
105
+ base = config.rope_parameters[layer_type]["rope_theta"]
106
+ partial_rotary_factor = config.rope_parameters[layer_type].get("partial_rotary_factor", 1.0)
107
+ dim = getattr(config, "head_dim", None) or config.hidden_size // config.num_attention_heads
108
+ rope_angles = int(partial_rotary_factor * dim) // 2
109
+ inv_freq = 1.0 / (base ** (torch.arange(0, 2 * rope_angles, 2, dtype=torch.float) / dim))
110
+ inv_freq = torch.cat((inv_freq, torch.zeros(dim // 2 - rope_angles, dtype=torch.float)))
111
+ return inv_freq.to(device), 1.0
112
+
113
+ @torch.no_grad()
114
+ @dynamic_rope_update # power user: used with advanced RoPE types (e.g. dynamic rope)
115
+ def forward(self, x, position_ids, layer_type):
116
+ inv_freq = getattr(self, f"{layer_type}_inv_freq")
117
+ attention_scaling = getattr(self, f"{layer_type}_attention_scaling")
118
+
119
+ freqs = position_ids[..., None].float() * inv_freq.to(device=x.device, dtype=torch.float)
120
+ emb = torch.cat((freqs, freqs), dim=-1)
121
+ cos = emb.cos() * attention_scaling
122
+ sin = emb.sin() * attention_scaling
123
+
124
+ return cos.to(dtype=x.dtype), sin.to(dtype=x.dtype)
125
+
126
+ @classmethod
127
+ def compute_rope_parameters(
128
+ cls, config: Lfm3Config, device=None, layer_type: str | None = None
129
+ ) -> tuple[torch.Tensor, float]:
130
+ """Inverse frequencies of any RoPE type in the zero-tail layout: scaled types are evaluated over the full head
131
+ dimension and truncated to the rotated prefix."""
132
+ rope_params = config.rope_parameters[layer_type]
133
+ if rope_params["rope_type"] == "default":
134
+ return cls.compute_default_rope_parameters(config, device, layer_type=layer_type)
135
+ full_config = copy.copy(config)
136
+ full_config.rope_parameters = {
137
+ **config.rope_parameters,
138
+ layer_type: {**rope_params, "partial_rotary_factor": 1.0},
139
+ }
140
+ inv_freq, attention_factor = ROPE_INIT_FUNCTIONS[rope_params["rope_type"]](
141
+ full_config, device, layer_type=layer_type
142
+ )
143
+ dim = getattr(config, "head_dim", None) or config.hidden_size // config.num_attention_heads
144
+ rope_angles = int(rope_params.get("partial_rotary_factor", 1.0) * dim) // 2
145
+ inv_freq = torch.cat((inv_freq[:rope_angles], inv_freq.new_zeros(dim // 2 - rope_angles)))
146
+ return inv_freq, attention_factor
147
+
148
+
149
+ class Lfm3MLP(nn.Module):
150
+ def __init__(self, config: Lfm3Config):
151
+ super().__init__()
152
+ self.w1 = nn.Linear(config.hidden_size, config.intermediate_size, bias=False)
153
+ self.w3 = nn.Linear(config.hidden_size, config.intermediate_size, bias=False)
154
+ self.w2 = nn.Linear(config.intermediate_size, config.hidden_size, bias=False)
155
+
156
+ def forward(self, x):
157
+ return self.w2(F.silu(self.w1(x)) * self.w3(x))
158
+
159
+
160
+ def rotate_half(x):
161
+ """Rotates half the hidden dims of the input."""
162
+ x1 = x[..., : x.shape[-1] // 2]
163
+ x2 = x[..., x.shape[-1] // 2 :]
164
+ return torch.cat((-x2, x1), dim=-1)
165
+
166
+
167
+ def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
168
+ """
169
+ This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
170
+ num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
171
+ """
172
+ batch, num_key_value_heads, slen, head_dim = hidden_states.shape
173
+ if n_rep == 1:
174
+ return hidden_states
175
+ hidden_states = hidden_states[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim)
176
+ return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
177
+
178
+
179
+ def eager_attention_forward(
180
+ module: nn.Module,
181
+ query: torch.Tensor,
182
+ key: torch.Tensor,
183
+ value: torch.Tensor,
184
+ attention_mask: torch.Tensor | None,
185
+ scaling: float,
186
+ dropout: float = 0.0,
187
+ **kwargs: Unpack[TransformersKwargs],
188
+ ):
189
+ key_states = repeat_kv(key, module.num_key_value_groups)
190
+ value_states = repeat_kv(value, module.num_key_value_groups)
191
+
192
+ attn_weights = torch.matmul(query, key_states.transpose(2, 3)) * scaling
193
+ if attention_mask is not None:
194
+ attn_weights = attn_weights + attention_mask
195
+
196
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query.dtype)
197
+ attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
198
+ attn_output = torch.matmul(attn_weights, value_states)
199
+ attn_output = attn_output.transpose(1, 2).contiguous()
200
+
201
+ return attn_output, attn_weights
202
+
203
+
204
+ def apply_rotary_pos_emb(q, k, cos, sin, unsqueeze_dim=1):
205
+ """Applies RoPE to the query and key tensors in float32, like the reference implementation."""
206
+ cos = cos.unsqueeze(unsqueeze_dim).float()
207
+ sin = sin.unsqueeze(unsqueeze_dim).float()
208
+ q_embed = (q.float() * cos) + (rotate_half(q.float()) * sin)
209
+ k_embed = (k.float() * cos) + (rotate_half(k.float()) * sin)
210
+ return q_embed.to(q.dtype), k_embed.to(k.dtype)
211
+
212
+
213
+ class Lfm3Attention(nn.Module):
214
+ """Llama attention with QK RMSNorm, a sigmoid output gate per head, a sliding window on `sliding_attention`
215
+ layers and RoPE in float32."""
216
+
217
+ def __init__(self, config: Lfm3Config, layer_idx: int):
218
+ super().__init__()
219
+ self.config = config
220
+ self.layer_idx = layer_idx
221
+ self.head_dim = getattr(config, "head_dim", config.hidden_size // config.num_attention_heads)
222
+ self.num_key_value_groups = config.num_attention_heads // config.num_key_value_heads
223
+ self.scaling = self.head_dim**-0.5
224
+ self.is_causal = True
225
+ self.q_proj = nn.Linear(config.hidden_size, config.num_attention_heads * self.head_dim, bias=False)
226
+ self.k_proj = nn.Linear(config.hidden_size, config.num_key_value_heads * self.head_dim, bias=False)
227
+ self.v_proj = nn.Linear(config.hidden_size, config.num_key_value_heads * self.head_dim, bias=False)
228
+ self.o_proj = nn.Linear(config.num_attention_heads * self.head_dim, config.hidden_size, bias=False)
229
+ self.q_norm = Lfm3RMSNorm(self.head_dim, eps=config.norm_eps)
230
+ self.k_norm = Lfm3RMSNorm(self.head_dim, eps=config.norm_eps)
231
+ self.gate_proj = nn.Linear(config.hidden_size, config.num_attention_heads, bias=False)
232
+ self.sliding_window = config.sliding_window if config.layer_types[layer_idx] == "sliding_attention" else None
233
+
234
+ def forward(
235
+ self,
236
+ hidden_states: torch.Tensor,
237
+ position_embeddings: tuple[torch.Tensor, torch.Tensor],
238
+ attention_mask: torch.Tensor | None,
239
+ past_key_values: Cache | None = None,
240
+ **kwargs: Unpack[TransformersKwargs],
241
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
242
+ input_shape = hidden_states.shape[:-1]
243
+ hidden_shape = (*input_shape, -1, self.head_dim)
244
+
245
+ query_states = self.q_norm(self.q_proj(hidden_states).view(*hidden_shape)).transpose(1, 2)
246
+ key_states = self.k_norm(self.k_proj(hidden_states).view(*hidden_shape)).transpose(1, 2)
247
+ value_states = self.v_proj(hidden_states).view(*hidden_shape).transpose(1, 2)
248
+
249
+ cos, sin = position_embeddings
250
+ query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin)
251
+
252
+ if past_key_values is not None:
253
+ key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx)
254
+
255
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
256
+ self.config._attn_implementation, eager_attention_forward
257
+ )
258
+
259
+ attn_output, attn_weights = attention_interface(
260
+ self,
261
+ query_states,
262
+ key_states,
263
+ value_states,
264
+ attention_mask,
265
+ dropout=0.0,
266
+ scaling=self.scaling,
267
+ sliding_window=self.sliding_window,
268
+ **kwargs,
269
+ )
270
+ gate = torch.sigmoid(self.gate_proj(hidden_states)).unsqueeze(-1)
271
+ attn_output = (attn_output * gate).reshape(*input_shape, -1).contiguous()
272
+ attn_output = self.o_proj(attn_output)
273
+ return attn_output, attn_weights
274
+
275
+
276
+ def apply_mask_to_padding_states(hidden_states, attention_mask):
277
+ """
278
+ Tunes out the hidden states for padding tokens, see https://github.com/state-spaces/mamba/issues/66
279
+ """
280
+ # NOTE: attention mask is a 2D boolean tensor
281
+ if attention_mask is not None:
282
+ dtype = hidden_states.dtype
283
+ hidden_states = (hidden_states * attention_mask[:, :, None]).to(dtype)
284
+
285
+ return hidden_states
286
+
287
+
288
+ @use_kernel_func_from_hub_with_fallback("causal_conv1d_update", "causal_conv1d")
289
+ def causal_conv1d_update(
290
+ hidden_states: torch.Tensor,
291
+ conv_state: torch.Tensor,
292
+ weight: nn.Parameter,
293
+ bias: nn.Parameter | None = None,
294
+ activation: str | None = None,
295
+ ):
296
+ _, hidden_size, seq_len = hidden_states.shape
297
+ state_len = conv_state.shape[-1]
298
+
299
+ hidden_states_new = torch.cat([conv_state, hidden_states], dim=-1).to(weight.dtype)
300
+ conv_state.copy_(hidden_states_new[:, :, -state_len:])
301
+ out = F.conv1d(hidden_states_new, weight.unsqueeze(1), bias, padding=0, groups=hidden_size)
302
+ out = out[:, :, -seq_len:]
303
+ if activation is not None:
304
+ out = ACT2FN[activation](out)
305
+ return out.to(hidden_states.dtype)
306
+
307
+
308
+ @use_kernel_func_from_hub_with_fallback("causal_conv1d_fn", "causal_conv1d")
309
+ def causal_conv1d_fn(
310
+ hidden_states: torch.Tensor,
311
+ weight: nn.Parameter,
312
+ bias: nn.Parameter | None = None,
313
+ activation: str | None = None,
314
+ **kwargs,
315
+ ):
316
+ _, hidden_size, seq_len = hidden_states.shape
317
+ padding = weight.shape[-1] - 1
318
+
319
+ out = F.conv1d(
320
+ hidden_states.to(weight.dtype),
321
+ weight=weight.unsqueeze(1),
322
+ bias=bias,
323
+ padding=padding,
324
+ groups=hidden_size,
325
+ )[:, :, :seq_len]
326
+ if activation is not None:
327
+ out = ACT2FN[activation](out)
328
+ return out.to(hidden_states.dtype)
329
+
330
+
331
+ @use_kernelized_func([causal_conv1d_update, causal_conv1d_fn])
332
+ class Lfm3ShortConv(nn.Module):
333
+ """LFM2 gated short convolution with packed-document boundaries (`seq_idx`), the padding mask re-applied after a
334
+ biased `in_proj`, and width-7 Triton inference kernels with a PyTorch fallback."""
335
+
336
+ def __init__(
337
+ self,
338
+ config: Lfm3Config,
339
+ layer_idx: int,
340
+ ):
341
+ super().__init__()
342
+ self.config = config
343
+ self.layer_idx = layer_idx
344
+ self.conv_kernel_size = config.conv_L_cache
345
+ self.bias = config.conv_bias
346
+
347
+ self.conv = nn.Conv1d(
348
+ in_channels=config.hidden_size,
349
+ out_channels=config.hidden_size,
350
+ kernel_size=self.conv_kernel_size,
351
+ groups=config.hidden_size,
352
+ bias=self.bias,
353
+ padding=self.conv_kernel_size - 1,
354
+ )
355
+ self.in_proj = nn.Linear(config.hidden_size, 3 * config.hidden_size, bias=self.bias)
356
+ self.out_proj = nn.Linear(config.hidden_size, config.hidden_size, bias=self.bias)
357
+
358
+ self.layer_type = config.layer_types[layer_idx]
359
+
360
+ @force_accelerate_hooks("conv")
361
+ def forward(
362
+ self,
363
+ hidden_states: torch.Tensor,
364
+ past_key_values: Cache | None = None,
365
+ attention_mask: torch.Tensor | None = None,
366
+ seq_idx: torch.IntTensor | None = None,
367
+ ):
368
+ seq_len = hidden_states.shape[1]
369
+
370
+ hidden_states = apply_mask_to_padding_states(hidden_states, attention_mask)
371
+ BCx = self.in_proj(hidden_states).transpose(-1, -2)
372
+ B, C, x = BCx.chunk(3, dim=-2)
373
+ hidden_states = B * x
374
+ if attention_mask is not None:
375
+ # with `conv_bias` the projection makes the padded positions non-zero again
376
+ hidden_states = hidden_states * attention_mask[:, None, :].to(hidden_states.dtype)
377
+
378
+ use_precomputed_states = past_key_values is not None and past_key_values.has_previous_state(self.layer_idx)
379
+ use_kernels = 2 <= self.conv_kernel_size <= 4
380
+ use_wide_kernel = can_use_lfm3_conv(hidden_states, self.conv.weight)
381
+
382
+ if use_precomputed_states and seq_len == 1 and not past_key_values.layers[self.layer_idx].record_past:
383
+ conv_state = past_key_values.layers[self.layer_idx].conv_states[0]
384
+ if use_wide_kernel and conv_state.shape[-1] in (6, 7):
385
+ hidden_states = lfm3_causal_conv1d_update(
386
+ hidden_states, conv_state, self.conv.weight.squeeze(1), self.conv.bias
387
+ )
388
+ elif use_kernels:
389
+ hidden_states = causal_conv1d_update(
390
+ hidden_states, conv_state, self.conv.weight.squeeze(1), self.conv.bias
391
+ )
392
+ else:
393
+ hidden_states = torch.cat([conv_state, hidden_states], dim=-1)
394
+ conv_state.copy_(hidden_states[:, :, -conv_state.shape[-1] :])
395
+ hidden_states = self._conv(hidden_states)[:, :, -seq_len:]
396
+ else:
397
+ if past_key_values is not None and seq_idx is not None:
398
+ # `seq_idx` is only set on an empty cache, and the decode steps read the cached window without it:
399
+ # only the last document (after any positional left padding) may enter the window
400
+ last_document = (seq_idx == seq_idx[:, -1:])[:, None].to(hidden_states.dtype)
401
+ past_key_values.update_conv_state(
402
+ hidden_states * last_document, self.layer_idx, conv_kernel_size=self.conv_kernel_size
403
+ )
404
+ elif past_key_values is not None:
405
+ hidden_states = past_key_values.update_conv_state(
406
+ hidden_states, self.layer_idx, conv_kernel_size=self.conv_kernel_size
407
+ )
408
+
409
+ if use_wide_kernel:
410
+ hidden_states = lfm3_causal_conv1d(hidden_states, self.conv.weight.squeeze(1), self.conv.bias, seq_idx)
411
+ elif seq_idx is not None:
412
+ hidden_states = self._segmented_conv(hidden_states, seq_idx)
413
+ elif use_kernels:
414
+ hidden_states = causal_conv1d_fn(hidden_states, self.conv.weight.squeeze(1), self.conv.bias)
415
+ else:
416
+ hidden_states = self._conv(hidden_states)
417
+
418
+ # Drop the additional previous states
419
+ if past_key_values is not None:
420
+ hidden_states = hidden_states[:, :, -seq_len:]
421
+
422
+ y = C * hidden_states
423
+ y = y.transpose(-1, -2).contiguous()
424
+ y = self.out_proj(y)
425
+ return y
426
+
427
+ def _conv(self, hidden_states: torch.Tensor) -> torch.Tensor:
428
+ """Causal depthwise conv in torch: the `causal_conv1d` kernels only accept widths 2 to 4."""
429
+ conv_out = self.conv(hidden_states.to(self.conv.weight.dtype))[..., : hidden_states.shape[-1]]
430
+ return conv_out.to(hidden_states.dtype)
431
+
432
+ def _segmented_conv(self, hidden_states: torch.Tensor, seq_idx: torch.Tensor) -> torch.Tensor:
433
+ """Causal depthwise conv that only reads positions sharing the current token's document index."""
434
+ kernel_size = self.conv_kernel_size
435
+ seq_idx = F.pad(seq_idx, (hidden_states.shape[-1] - seq_idx.shape[-1] + kernel_size - 1, 0), value=-1)
436
+ windows = F.pad(hidden_states, (kernel_size - 1, 0)).unfold(-1, kernel_size, 1)
437
+ seq_idx_windows = seq_idx.unfold(-1, kernel_size, 1)
438
+ same_document = (seq_idx_windows == seq_idx_windows[..., -1:]).unsqueeze(1)
439
+ conv_out = torch.einsum("bdtk,dk->bdt", windows * same_document.to(windows.dtype), self.conv.weight.squeeze(1))
440
+ if self.bias:
441
+ conv_out = conv_out + self.conv.bias[None, :, None]
442
+ return conv_out.to(hidden_states.dtype)
443
+
444
+
445
+ class Lfm3DecoderLayer(GradientCheckpointingLayer):
446
+ def __init__(self, config: Lfm3Config, layer_idx: int):
447
+ super().__init__()
448
+ self.is_attention_layer = config.layer_types[layer_idx] in config._attention_layer_types
449
+ self.use_sandwich_norm = config.use_sandwich_norm
450
+
451
+ if self.is_attention_layer:
452
+ self.self_attn = Lfm3Attention(config, layer_idx)
453
+ else:
454
+ self.conv = Lfm3ShortConv(config, layer_idx)
455
+ self.feed_forward = Lfm3MLP(config)
456
+ self.operator_norm = Lfm3RMSNorm(config.hidden_size, eps=config.norm_eps)
457
+ self.ffn_norm = Lfm3RMSNorm(config.hidden_size, eps=config.norm_eps)
458
+ if self.use_sandwich_norm:
459
+ self.post_operator_norm = Lfm3RMSNorm(config.hidden_size, eps=config.norm_eps)
460
+ self.post_ffn_norm = Lfm3RMSNorm(config.hidden_size, eps=config.norm_eps)
461
+
462
+ def forward(
463
+ self,
464
+ hidden_states: torch.Tensor,
465
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
466
+ attention_mask: torch.Tensor | None = None,
467
+ position_ids: torch.LongTensor | None = None,
468
+ past_key_values: Cache | None = None,
469
+ seq_idx: torch.IntTensor | None = None,
470
+ **kwargs,
471
+ ) -> torch.Tensor:
472
+ residual = hidden_states
473
+ if self.is_attention_layer:
474
+ hidden_states, _ = self.self_attn(
475
+ hidden_states=self.operator_norm(hidden_states),
476
+ position_embeddings=position_embeddings,
477
+ attention_mask=attention_mask,
478
+ position_ids=position_ids,
479
+ past_key_values=past_key_values,
480
+ **kwargs,
481
+ )
482
+ else:
483
+ hidden_states = self.conv(
484
+ hidden_states=self.operator_norm(hidden_states),
485
+ past_key_values=past_key_values,
486
+ attention_mask=attention_mask,
487
+ seq_idx=seq_idx,
488
+ )
489
+ if self.use_sandwich_norm:
490
+ hidden_states = self.post_operator_norm(hidden_states)
491
+ hidden_states = hidden_states + residual
492
+
493
+ ffn_out = self.feed_forward(self.ffn_norm(hidden_states))
494
+ if self.use_sandwich_norm:
495
+ ffn_out = self.post_ffn_norm(ffn_out)
496
+ hidden_states = hidden_states + ffn_out
497
+
498
+ return hidden_states
499
+
500
+
501
+ def create_masks_by_layer_type(
502
+ config: PreTrainedConfig,
503
+ inputs_embeds: torch.Tensor,
504
+ attention_mask: torch.Tensor | None,
505
+ past_key_values: Cache | None,
506
+ position_ids: torch.Tensor | None = None,
507
+ ) -> dict:
508
+ """The cache sizes each mask from the layer it is asked about, and the generic mask utilities ask about the first
509
+ non-sliding layer, which for a leading `conv` layer redirects to the first attention layer of any type: name the
510
+ first layer of each mask's own type instead."""
511
+ config = config.get_text_config()
512
+ mask_kwargs = {
513
+ "config": config,
514
+ "inputs_embeds": inputs_embeds,
515
+ "attention_mask": attention_mask,
516
+ "past_key_values": past_key_values,
517
+ "position_ids": position_ids,
518
+ }
519
+ layer_types, causal_mask_mapping = config.layer_types, {}
520
+ if "full_attention" in layer_types:
521
+ layer_idx = layer_types.index("full_attention")
522
+ causal_mask_mapping["full_attention"] = create_causal_mask(**mask_kwargs, layer_idx=layer_idx)
523
+ if "sliding_attention" in layer_types:
524
+ layer_idx = layer_types.index("sliding_attention")
525
+ causal_mask_mapping["sliding_attention"] = create_sliding_window_causal_mask(
526
+ **mask_kwargs, layer_idx=layer_idx
527
+ )
528
+ if "conv" in layer_types:
529
+ causal_mask_mapping["conv"] = create_recurrent_attention_mask(**mask_kwargs)
530
+ return causal_mask_mapping
531
+
532
+
533
+ @auto_docstring
534
+ class Lfm3PreTrainedModel(PreTrainedModel):
535
+ config: Lfm3Config
536
+ base_model_prefix = "model"
537
+ supports_gradient_checkpointing = True
538
+ _no_split_modules = ["Lfm3DecoderLayer"]
539
+ _skip_keys_device_placement = ["past_key_values"]
540
+ _supports_flash_attn = True
541
+ _supports_sdpa = True
542
+ _supports_flex_attn = True
543
+
544
+ _can_compile_fullgraph = True
545
+ _supports_attention_backend = True
546
+ _can_record_outputs = {
547
+ "hidden_states": Lfm3DecoderLayer,
548
+ "attentions": Lfm3Attention,
549
+ }
550
+
551
+ @torch.no_grad()
552
+ def _init_weights(self, module):
553
+ super()._init_weights(module)
554
+ if isinstance(module, Lfm3RotaryEmbedding):
555
+ for layer_type in module.layer_types:
556
+ curr_inv_freq, _ = module.compute_rope_parameters(module.config, layer_type=layer_type)
557
+ init.copy_(getattr(module, f"{layer_type}_inv_freq"), curr_inv_freq)
558
+ init.copy_(getattr(module, f"{layer_type}_original_inv_freq"), curr_inv_freq)
559
+
560
+ @staticmethod
561
+ def create_masks_for_generate(
562
+ config: PreTrainedConfig,
563
+ inputs_embeds: torch.Tensor,
564
+ attention_mask: torch.Tensor | None,
565
+ past_key_values: Cache | None,
566
+ position_ids: torch.Tensor | None = None,
567
+ **kwargs,
568
+ ) -> dict:
569
+ return create_masks_by_layer_type(config, inputs_embeds, attention_mask, past_key_values, position_ids)
570
+
571
+
572
+ def packed_under_padding(position_ids: torch.Tensor, padding_mask: torch.Tensor, filled) -> torch.Tensor:
573
+ """Padding repeats position 0, so packing is a restart between two kept tokens, or a kept position 0 on a filled
574
+ cache whose row holds a kept token; attention under a padding mask cannot isolate it."""
575
+ restarts = torch.diff(position_ids, prepend=position_ids[:, :1] - 1, dim=-1) != 1
576
+ valid = padding_mask[:, -restarts.shape[-1] :].bool()
577
+ packed = restarts[:, 1:] & valid[:, 1:] & valid[:, :-1]
578
+ if filled:
579
+ # a row whose cached tokens are all padding (chunked prefill of a left-padded batch) has no document to pack
580
+ # against; a mask covering only the chunk keeps the strict rule
581
+ cached = padding_mask[:, : padding_mask.shape[-1] - restarts.shape[-1]]
582
+ cached_valid = cached.bool().any(-1, keepdim=True) if cached.shape[-1] else torch.ones_like(valid[:, :1])
583
+ packed = packed | (valid[:, :1] & (position_ids[:, :1] == 0) & cached_valid)
584
+ return packed
585
+
586
+
587
+ @auto_docstring
588
+ class Lfm3Model(Lfm3PreTrainedModel):
589
+ """LFM2 model with sliding-attention layers as a third layer type: the mask and the rotary embedding are built per
590
+ layer type, and packed documents (`position_ids` restarting) reset the short-conv window through `seq_idx`."""
591
+
592
+ def __init__(self, config: Lfm3Config):
593
+ super().__init__(config)
594
+ self.padding_idx = config.pad_token_id
595
+ self.vocab_size = config.vocab_size
596
+
597
+ self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)
598
+ self.layers = nn.ModuleList(
599
+ [Lfm3DecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
600
+ )
601
+ self.rotary_emb = Lfm3RotaryEmbedding(config=config)
602
+ self.gradient_checkpointing = False
603
+ self.embedding_norm = Lfm3RMSNorm(config.hidden_size, eps=config.norm_eps)
604
+
605
+ # Initialize weights and apply final processing
606
+ self.post_init()
607
+
608
+ @merge_with_config_defaults
609
+ @capture_outputs
610
+ @auto_docstring
611
+ def forward(
612
+ self,
613
+ input_ids: torch.LongTensor | None = None,
614
+ attention_mask: torch.Tensor | None = None,
615
+ position_ids: torch.LongTensor | None = None,
616
+ past_key_values: Cache | None = None,
617
+ inputs_embeds: torch.FloatTensor | None = None,
618
+ use_cache: bool | None = None,
619
+ **kwargs: Unpack[TransformersKwargs],
620
+ ) -> BaseModelOutputWithPast:
621
+ r"""
622
+ attention_mask (`torch.Tensor` of shape `(batch_size, sequence_length)` or `dict`, *optional*):
623
+ Mask to avoid performing attention on padding token indices (1 keeps a token, 0 masks it), or the mask
624
+ dict of `create_masks_for_generate`. A mask dict is used as given: `generate` refuses packed
625
+ `position_ids` before its prefill.
626
+ """
627
+ if (input_ids is None) ^ (inputs_embeds is not None):
628
+ raise ValueError("You must specify exactly one of input_ids or inputs_embeds")
629
+ # `seq_idx` is a shared kwarg (`DataCollatorWithFlattening(return_seq_idx=True)`, read by Lfm2); LFM3 derives
630
+ # the documents from the restarting `position_ids` that come with it
631
+ if kwargs.pop("seq_idx", None) is not None and position_ids is None:
632
+ raise ValueError("Pass packed documents as `position_ids` restarting at 0; LFM3 does not read `seq_idx`")
633
+
634
+ if inputs_embeds is None:
635
+ inputs_embeds = self.embed_tokens(input_ids)
636
+
637
+ if use_cache and past_key_values is None:
638
+ past_key_values = DynamicCache(config=self.config)
639
+ filled = past_key_values is not None and (
640
+ past_key_values.get_seq_length() > 0
641
+ if any(t != "conv" for t in self.config.layer_types)
642
+ else past_key_values.has_previous_state()
643
+ )
644
+ packed_cache = getattr(past_key_values, "lfm3_packed", None)
645
+ if isinstance(packed_cache, torch.Tensor) or packed_cache:
646
+ # Reset layers have no history to mix. Ordinary caches carry a Python `False` and skip this, so the decode
647
+ # steps do no tensor work here; only a compiled packed prefill leaves a tensor flag, and `filled` stays
648
+ # tensor-valued for static caches.
649
+ packed_message = (
650
+ "A cache filled by a packed-document forward cannot be continued: its attention spans the documents"
651
+ )
652
+ packed_cache = packed_cache & filled if isinstance(packed_cache, torch.Tensor) else filled
653
+ if isinstance(packed_cache, torch.Tensor):
654
+ torch_compilable_check(~packed_cache, packed_message)
655
+ if not is_tracing(packed_cache):
656
+ past_key_values.lfm3_packed = bool(packed_cache)
657
+ elif packed_cache:
658
+ raise ValueError(packed_message)
659
+ else:
660
+ past_key_values.lfm3_packed = False
661
+
662
+ # The mask utilities detect packing only with `attention_mask=None`, so a multi-token forward with explicit
663
+ # positions drops the all-ones mask tokenizers emit (a data-dependent check, hence not when traced).
664
+ explicit_positions = position_ids is not None and inputs_embeds.shape[1] > 1
665
+ if (
666
+ explicit_positions
667
+ and isinstance(attention_mask, torch.Tensor)
668
+ and attention_mask.ndim == 2
669
+ and not is_tracing(position_ids)
670
+ and bool(attention_mask.all())
671
+ ):
672
+ attention_mask = None
673
+ seq_idx = None
674
+ if position_ids is None:
675
+ past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
676
+ position_ids = torch.arange(inputs_embeds.shape[1], device=inputs_embeds.device) + past_seen_tokens
677
+ position_ids = position_ids.unsqueeze(0)
678
+ elif explicit_positions and not isinstance(attention_mask, dict):
679
+ # a mask dict comes from `generate`, which refused packing before its prefill
680
+ if isinstance(attention_mask, torch.Tensor) and attention_mask.ndim == 2:
681
+ # data-dependent: a traced forward skips it (`generate` refuses packing eagerly before its prefill)
682
+ if not is_tracing(position_ids):
683
+ torch_compilable_check(
684
+ ~packed_under_padding(position_ids, attention_mask, filled),
685
+ "Packed documents (`position_ids` restarting) cannot be combined with an attention mask: "
686
+ "attention would not isolate the documents. Pass `attention_mask=None` without a static cache.",
687
+ )
688
+ else:
689
+ restarts = torch.diff(position_ids, prepend=position_ids[:, :1] - 1, dim=-1) != 1
690
+ document_ids = restarts.cumsum(-1)
691
+ if is_tracing(position_ids) or bool((document_ids[..., -1] > 0).any()):
692
+ seq_idx = document_ids.to(torch.int32)
693
+ # Packed documents are prefilled in one forward on an empty cache (cached tokens would join the first
694
+ # document), and their cache is never continued
695
+ if filled:
696
+ torch_compilable_check(
697
+ (~restarts[:, 1:]).all() & (position_ids[:, 0] != 0).all(),
698
+ "Packed documents must be prefilled in one forward; the cache already holds tokens",
699
+ )
700
+ # Ordinary continuation must retain the convolution window from the cached document.
701
+ seq_idx = None
702
+ if attention_mask is None:
703
+ # without a mask only the cacheless packed-sequence mask isolates repeated zero positions (left
704
+ # padding): a cache-sized mask and every later decode step would attend them, so they count too
705
+ packed = (document_ids[..., -1] > 0).any()
706
+ else:
707
+ # a prepared 4D mask is the caller's and carries the padding (repeated zero positions), but may
708
+ # or may not isolate documents, so the cache records the packing and is never continued
709
+ steps = torch.diff(position_ids, dim=-1)
710
+ packed = ((steps != 1) & ~((steps == 0) & (position_ids[:, :-1] == 0))).any()
711
+ if past_key_values is not None:
712
+ # resolved here, where the prefill already syncs, so the decode steps carry a Python flag: no
713
+ # per-step host sync, data-dependent op or CUDA-graph output on the cache
714
+ past_key_values.lfm3_packed = packed if is_tracing(position_ids) else bool(packed)
715
+
716
+ if not isinstance(causal_mask_mapping := attention_mask, dict):
717
+ mask_kwargs = {
718
+ "config": self.config,
719
+ "inputs_embeds": inputs_embeds,
720
+ "attention_mask": attention_mask,
721
+ "past_key_values": past_key_values,
722
+ "position_ids": position_ids,
723
+ }
724
+ # The mask utilities only isolate packed documents without a cache, and an empty cache adds nothing.
725
+ layer_types = self.config.layer_types
726
+ if seq_idx is not None and past_key_values is not None:
727
+ attention_layer = next((i for i, t in enumerate(layer_types) if t != "conv"), None)
728
+ if attention_layer is not None and past_key_values.get_seq_length(attention_layer) == 0:
729
+ if past_key_values.is_compileable:
730
+ # static layers always return their full length, so the masks are sized by the cache, which
731
+ # isolates neither documents nor positional-only left padding
732
+ torch_compilable_check(
733
+ ~packed,
734
+ "Packed documents, or left padding without an attention mask, cannot be prefilled into a "
735
+ "static cache: its masks are sized by the cache. Pass the padding as `attention_mask`, or "
736
+ "use a `DynamicCache` for packed documents",
737
+ )
738
+ else:
739
+ mask_kwargs["past_key_values"] = None
740
+ causal_mask_mapping = create_masks_by_layer_type(**mask_kwargs)
741
+
742
+ hidden_states = inputs_embeds
743
+ position_embeddings = {
744
+ layer_type: self.rotary_emb(hidden_states, position_ids=position_ids, layer_type=layer_type)
745
+ for layer_type in self.rotary_emb.layer_types
746
+ }
747
+
748
+ for i, decoder_layer in enumerate(self.layers[: self.config.num_hidden_layers]):
749
+ layer_type = self.config.layer_types[i]
750
+ hidden_states = decoder_layer(
751
+ hidden_states,
752
+ attention_mask=causal_mask_mapping[layer_type],
753
+ position_embeddings=position_embeddings.get(layer_type),
754
+ position_ids=position_ids,
755
+ past_key_values=past_key_values,
756
+ seq_idx=seq_idx,
757
+ **kwargs,
758
+ )
759
+
760
+ hidden_states = self.embedding_norm(hidden_states)
761
+
762
+ return BaseModelOutputWithPast(
763
+ last_hidden_state=hidden_states,
764
+ past_key_values=past_key_values,
765
+ )
766
+
767
+
768
+ def reject_packed_generation(config: PreTrainedConfig, input_ids, generation_config, model_kwargs, is_first_iteration):
769
+ """`generate` decodes from the cache its prefill fills, and no mask it builds isolates packed documents
770
+ (`position_ids` restarting): the prompt is refused before the prefill, one forward or every chunk, writes to it."""
771
+ position_ids, attention_mask = model_kwargs.get("position_ids"), model_kwargs.get("attention_mask")
772
+ cache = model_kwargs.get("past_key_values")
773
+ if cache is None or position_ids is None or position_ids.ndim != 2:
774
+ return
775
+ if attention_mask is not None and attention_mask.ndim != 2:
776
+ return
777
+ has_attention = any(t != "conv" for t in config.get_text_config().layer_types)
778
+ filled = bool(cache.get_seq_length() > 0 if has_attention else cache.has_previous_state())
779
+ inputs_embeds = model_kwargs.get("inputs_embeds") if is_first_iteration else None
780
+ length = input_ids.shape[1] if inputs_embeds is None else inputs_embeds.shape[1]
781
+ if generation_config.prefill_chunk_size is not None:
782
+ # the chunks run from the start of the prompt
783
+ position_ids = position_ids[:, :length]
784
+ else:
785
+ if filled and has_attention:
786
+ # the slice `_prefill` feeds: a prompt that spans the cache is cut to its uncached part
787
+ mask_length = getattr(generation_config, "_mask_length", -1)
788
+ if attention_mask is not None:
789
+ mask_length = attention_mask.shape[1]
790
+ if inputs_embeds is not None or mask_length == length:
791
+ length -= int(cache.get_seq_length())
792
+ position_ids = position_ids[:, position_ids.shape[1] - length :]
793
+ if attention_mask is None:
794
+ attention_mask = torch.ones_like(position_ids)
795
+ if bool(packed_under_padding(position_ids, attention_mask, filled).any()):
796
+ raise ValueError(
797
+ "`generate` cannot decode packed documents (`position_ids` restarting, or starting at 0 on a cache that "
798
+ "holds tokens): attention would span them. Generate each document in its own row of a left-padded batch."
799
+ )
800
+
801
+
802
+ @auto_docstring
803
+ class Lfm3ForCausalLM(Lfm3PreTrainedModel, GenerationMixin):
804
+ _tied_weights_keys = {"lm_head.weight": "model.embed_tokens.weight"}
805
+ _tp_plan = {"lm_head": "colwise_gather_output"}
806
+ _pp_plan = {"lm_head": (["hidden_states"], ["logits"])}
807
+ _fsdp_plan = {"lm_head": "keep_full_weight"}
808
+
809
+ def __init__(self, config):
810
+ super().__init__(config)
811
+ self.model = Lfm3Model(config)
812
+ self.vocab_size = config.vocab_size
813
+ self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
814
+
815
+ # Initialize weights and apply final processing
816
+ self.post_init()
817
+
818
+ @can_return_tuple
819
+ @auto_docstring
820
+ def forward(
821
+ self,
822
+ input_ids: torch.LongTensor | None = None,
823
+ attention_mask: torch.Tensor | None = None,
824
+ position_ids: torch.LongTensor | None = None,
825
+ past_key_values: Cache | None = None,
826
+ inputs_embeds: torch.FloatTensor | None = None,
827
+ labels: torch.LongTensor | None = None,
828
+ use_cache: bool | None = None,
829
+ logits_to_keep: int | torch.Tensor = 0,
830
+ **kwargs: Unpack[TransformersKwargs],
831
+ ) -> CausalLMOutputWithPast:
832
+ r"""
833
+ Example:
834
+
835
+ ```python
836
+ >>> from transformers import AutoTokenizer, Lfm3ForCausalLM
837
+
838
+ >>> model = Lfm3ForCausalLM.from_pretrained("LiquidAI/LFM3-1.7B")
839
+ >>> tokenizer = AutoTokenizer.from_pretrained("LiquidAI/LFM3-1.7B")
840
+
841
+ >>> inputs = tokenizer.apply_chat_template(
842
+ ... [{"role": "user", "content": "What is C. elegans?"}], add_generation_prompt=True, return_tensors="pt"
843
+ ... )
844
+ >>> generate_ids = model.generate(**inputs, max_new_tokens=64)
845
+ >>> tokenizer.decode(generate_ids[0, inputs["input_ids"].shape[1] :], skip_special_tokens=True)
846
+ ```"""
847
+ outputs: BaseModelOutputWithPast = self.model(
848
+ input_ids=input_ids,
849
+ attention_mask=attention_mask,
850
+ position_ids=position_ids,
851
+ past_key_values=past_key_values,
852
+ inputs_embeds=inputs_embeds,
853
+ use_cache=use_cache,
854
+ **kwargs,
855
+ )
856
+
857
+ hidden_states = outputs.last_hidden_state
858
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
859
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
860
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
861
+
862
+ loss = None
863
+ if labels is not None:
864
+ loss = self.loss_function(logits=logits, labels=labels, vocab_size=self.config.vocab_size, **kwargs)
865
+
866
+ return CausalLMOutputWithPast(
867
+ loss=loss,
868
+ logits=logits,
869
+ past_key_values=outputs.past_key_values,
870
+ hidden_states=outputs.hidden_states,
871
+ attentions=outputs.attentions,
872
+ )
873
+
874
+ def _prefill(self, input_ids, generation_config, model_kwargs, is_first_iteration=True):
875
+ # every prefill passes here once before its first forward: a check per chunk would leave the earlier chunks
876
+ # in the cache, and a dynamic cache would only refuse packing at the first decode step
877
+ reject_packed_generation(self.config, input_ids, generation_config, model_kwargs, is_first_iteration)
878
+ return super()._prefill(input_ids, generation_config, model_kwargs, is_first_iteration=is_first_iteration)
879
+
880
+
881
+ __all__ = ["Lfm3ForCausalLM", "Lfm3Model", "Lfm3PreTrainedModel"]
modeling_lfm3_vl.py ADDED
@@ -0,0 +1,965 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2026 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ from collections.abc import Callable
15
+ from dataclasses import dataclass
16
+
17
+ import torch
18
+ import torch.nn.functional as F
19
+ from torch import nn
20
+
21
+ from transformers.activations import ACT2FN
22
+ from transformers.cache_utils import Cache
23
+ from transformers.configuration_utils import PreTrainedConfig
24
+ from transformers.generation import GenerationMixin
25
+ from transformers.integrations import use_kernel_forward_from_hub
26
+ from transformers.masking_utils import (
27
+ create_causal_mask,
28
+ create_recurrent_attention_mask,
29
+ create_sliding_window_causal_mask,
30
+ )
31
+ from transformers.modeling_layers import GradientCheckpointingLayer
32
+ from transformers.modeling_outputs import BaseModelOutputWithPast, BaseModelOutputWithPooling, CausalLMOutputWithPast
33
+ from transformers.modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
34
+ from transformers.processing_utils import Unpack
35
+ from transformers.utils import (
36
+ TransformersKwargs,
37
+ auto_docstring,
38
+ can_return_tuple,
39
+ torch_compilable_check,
40
+ )
41
+ from transformers.utils.generic import (
42
+ accepts_precomputed_kwargs,
43
+ is_flash_attention_requested,
44
+ maybe_autocast,
45
+ merge_with_config_defaults,
46
+ )
47
+ from transformers.utils.output_capturing import OutputRecorder, capture_outputs
48
+ from transformers.vision_utils import get_max_seqlen, get_vision_attention_seqlens, get_vision_position_ids
49
+
50
+ from .configuration_lfm3_vl import Lfm3VlConfig, Lfm3VlVisionConfig
51
+ from .modeling_lfm3 import Lfm3Model
52
+
53
+
54
+ @use_kernel_forward_from_hub("RMSNorm")
55
+ class Lfm3VlRMSNorm(nn.Module):
56
+ def __init__(self, hidden_size, eps: float = 1e-6) -> None:
57
+ """
58
+ Lfm3VlRMSNorm is equivalent to T5LayerNorm
59
+ """
60
+ super().__init__()
61
+ self.weight = nn.Parameter(torch.ones(hidden_size))
62
+ self.variance_epsilon = eps
63
+
64
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
65
+ input_dtype = hidden_states.dtype
66
+ hidden_states = hidden_states.to(torch.float32)
67
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
68
+ hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
69
+ return self.weight * hidden_states.to(input_dtype)
70
+
71
+ def extra_repr(self):
72
+ return f"{tuple(self.weight.shape)}, eps={self.variance_epsilon}"
73
+
74
+
75
+ class Lfm3VlMLP(nn.Module):
76
+ def __init__(self, config: Lfm3VlConfig):
77
+ super().__init__()
78
+ self.w1 = nn.Linear(config.hidden_size, config.intermediate_size, bias=False)
79
+ self.w3 = nn.Linear(config.hidden_size, config.intermediate_size, bias=False)
80
+ self.w2 = nn.Linear(config.intermediate_size, config.hidden_size, bias=False)
81
+
82
+ def forward(self, x):
83
+ return self.w2(F.silu(self.w1(x)) * self.w3(x))
84
+
85
+
86
+ class Lfm3VlVisionRotaryEmbedding(nn.Module):
87
+ """
88
+ Simple axial 2D rope with same freqs used for H and W grids. The freqs are
89
+ pre-computed using `head-dim//4` which is later used to concat H and W positions.
90
+ The final angles rotate over the whole head dim, no partial rotation involved.
91
+ """
92
+
93
+ def __init__(self, config: Lfm3VlVisionConfig):
94
+ super().__init__()
95
+ self.config = config
96
+
97
+ self.rope_type = self.config.rope_parameters["rope_type"]
98
+ rope_init_fn: Callable = self.compute_axial_rope_parameters
99
+ if self.rope_type != "axial":
100
+ raise ValueError(f"{self.__class__.__name__} supports only axial rope, but requested {self.rope_type}")
101
+ inv_freq, self.attention_scaling = rope_init_fn(self.config)
102
+
103
+ self.inv_freq = nn.Buffer(inv_freq, persistent=False)
104
+ self.original_inv_freq = nn.Buffer(inv_freq.clone(), persistent=False)
105
+
106
+ @staticmethod
107
+ def compute_axial_rope_parameters(config: Lfm3VlVisionConfig, **kwargs) -> tuple[torch.Tensor, float]:
108
+ """Inverse frequencies of the axial RoPE over half the head dimension per axis, with an attention scaling of 1."""
109
+ base = config.rope_parameters["rope_theta"]
110
+ spatial_dim = config.hidden_size // config.num_attention_heads // 2
111
+ inv_freq = 1.0 / (base ** (torch.arange(0, spatial_dim, 2, dtype=torch.float) / spatial_dim))
112
+ return inv_freq, 1.0
113
+
114
+ @torch.no_grad()
115
+ def forward(self, x, position_ids):
116
+ # position_ids: (2, N) — row 0 = h coords, row 1 = w coords
117
+ position_ids_expanded = position_ids[..., None].float()
118
+ device_type = x.device.type if isinstance(x.device.type, str) else "cpu"
119
+ with maybe_autocast(device_type=device_type, enabled=False):
120
+ freqs = position_ids_expanded * self.inv_freq.float()
121
+ cos = freqs.cos() * self.attention_scaling
122
+ sin = freqs.sin() * self.attention_scaling
123
+
124
+ cos = self.recomposition_frequencies(cos)
125
+ sin = self.recomposition_frequencies(sin)
126
+ return cos, sin
127
+
128
+ def recomposition_frequencies(self, freq):
129
+ """
130
+ Recompose the frequencies into the final spatial layout used per each grid.
131
+ """
132
+ freq_h, freq_w = freq[:, 0], freq[:, 1]
133
+ freq_hw = torch.cat([freq_h, freq_w], dim=-1)
134
+ return torch.cat([freq_hw, freq_hw], dim=-1)
135
+
136
+
137
+ def rotate_half(x):
138
+ """Rotates half the hidden dims of the input."""
139
+ x1 = x[..., : x.shape[-1] // 2]
140
+ x2 = x[..., x.shape[-1] // 2 :]
141
+ return torch.cat((-x2, x1), dim=-1)
142
+
143
+
144
+ def apply_rotary_pos_emb_vision(
145
+ q: torch.Tensor, k: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor
146
+ ) -> tuple[torch.Tensor, torch.Tensor]:
147
+ orig_q_dtype = q.dtype
148
+ orig_k_dtype = k.dtype
149
+ q, k = q.float(), k.float()
150
+ cos, sin = cos.unsqueeze(-2).float(), sin.unsqueeze(-2).float()
151
+ q_embed = (q * cos) + (rotate_half(q) * sin)
152
+ k_embed = (k * cos) + (rotate_half(k) * sin)
153
+ q_embed = q_embed.to(orig_q_dtype)
154
+ k_embed = k_embed.to(orig_k_dtype)
155
+ return q_embed, k_embed
156
+
157
+
158
+ def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
159
+ """
160
+ This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
161
+ num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
162
+ """
163
+ batch, num_key_value_heads, slen, head_dim = hidden_states.shape
164
+ if n_rep == 1:
165
+ return hidden_states
166
+ hidden_states = hidden_states[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim)
167
+ return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
168
+
169
+
170
+ def eager_attention_forward(
171
+ module: nn.Module,
172
+ query: torch.Tensor,
173
+ key: torch.Tensor,
174
+ value: torch.Tensor,
175
+ attention_mask: torch.Tensor | None,
176
+ scaling: float,
177
+ dropout: float = 0.0,
178
+ **kwargs: Unpack[TransformersKwargs],
179
+ ):
180
+ key_states = repeat_kv(key, module.num_key_value_groups)
181
+ value_states = repeat_kv(value, module.num_key_value_groups)
182
+
183
+ attn_weights = torch.matmul(query, key_states.transpose(2, 3)) * scaling
184
+ if attention_mask is not None:
185
+ attn_weights = attn_weights + attention_mask
186
+
187
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query.dtype)
188
+ attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
189
+ attn_output = torch.matmul(attn_weights, value_states)
190
+ attn_output = attn_output.transpose(1, 2).contiguous()
191
+
192
+ return attn_output, attn_weights
193
+
194
+
195
+ class Lfm3VlVisionAttention(nn.Module):
196
+ """Bidirectional grouped-query attention over the patches of one frame, with RMSNorm on the query and key heads."""
197
+
198
+ def __init__(self, config: Lfm3VlVisionConfig):
199
+ super().__init__()
200
+ self.config = config
201
+ self.num_heads = config.num_attention_heads
202
+ self.num_key_value_heads = config.num_key_value_heads
203
+ self.num_key_value_groups = config.num_attention_heads // config.num_key_value_heads
204
+ self.head_dim = config.hidden_size // config.num_attention_heads
205
+ self.scaling = self.head_dim**-0.5
206
+ self.attention_dropout = 0.0
207
+ self.is_causal = False
208
+ self.q_proj = nn.Linear(config.hidden_size, self.num_heads * self.head_dim, bias=False)
209
+ self.k_proj = nn.Linear(config.hidden_size, self.num_key_value_heads * self.head_dim, bias=False)
210
+ self.v_proj = nn.Linear(config.hidden_size, self.num_key_value_heads * self.head_dim, bias=False)
211
+ self.out_proj = nn.Linear(self.num_heads * self.head_dim, config.hidden_size, bias=False)
212
+ self.q_layernorm = Lfm3VlRMSNorm(self.head_dim, eps=config.norm_eps)
213
+ self.k_layernorm = Lfm3VlRMSNorm(self.head_dim, eps=config.norm_eps)
214
+
215
+ def forward(
216
+ self,
217
+ hidden_states: torch.Tensor,
218
+ cu_seqlens: torch.Tensor,
219
+ position_embeddings: tuple[torch.Tensor, torch.Tensor],
220
+ max_seqlen: int | None = None,
221
+ frame_shapes: list[list[int]] | None = None,
222
+ **kwargs,
223
+ ) -> torch.Tensor:
224
+ seq_length = hidden_states.shape[0]
225
+ query_states = self.q_layernorm(self.q_proj(hidden_states).view(seq_length, self.num_heads, self.head_dim))
226
+ key_states = self.k_layernorm(
227
+ self.k_proj(hidden_states).view(seq_length, self.num_key_value_heads, self.head_dim)
228
+ )
229
+ value_states = self.v_proj(hidden_states).view(seq_length, self.num_key_value_heads, self.head_dim)
230
+
231
+ cos, sin = position_embeddings
232
+ query_states, key_states = apply_rotary_pos_emb_vision(query_states, key_states, cos, sin)
233
+
234
+ query_states = query_states.transpose(0, 1).unsqueeze(0)
235
+ key_states = key_states.transpose(0, 1).unsqueeze(0)
236
+ value_states = value_states.transpose(0, 1).unsqueeze(0)
237
+
238
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
239
+ self.config._attn_implementation, eager_attention_forward
240
+ )
241
+
242
+ if is_flash_attention_requested(self.config):
243
+ max_seqlen = get_max_seqlen(cu_seqlens, self.config, kwargs={"max_seqlen": max_seqlen})
244
+ attn_output, attn_weights = attention_interface(
245
+ self,
246
+ query_states,
247
+ key_states,
248
+ value_states,
249
+ attention_mask=None,
250
+ scaling=self.scaling,
251
+ dropout=0.0,
252
+ cu_seq_lens_q=cu_seqlens,
253
+ cu_seq_lens_k=cu_seqlens,
254
+ max_length_q=max_seqlen,
255
+ max_length_k=max_seqlen,
256
+ is_causal=False,
257
+ **kwargs,
258
+ )
259
+ else:
260
+ # Other implementations: frames of one shape (every video, equal-size images) attend as one batch, mixed
261
+ # shapes frame by frame. `frame_shapes` gives the lengths without a device sync.
262
+ if frame_shapes is not None:
263
+ lengths = [height * width for height, width in frame_shapes]
264
+ else:
265
+ lengths = (cu_seqlens[1:] - cu_seqlens[:-1]).tolist()
266
+ if len(set(lengths)) == 1:
267
+ query_states, key_states, value_states = (
268
+ tensor.reshape(-1, len(lengths), lengths[0], self.head_dim).transpose(0, 1)
269
+ for tensor in (query_states, key_states, value_states)
270
+ )
271
+ attn_output, attn_weights = attention_interface(
272
+ self,
273
+ query_states,
274
+ key_states,
275
+ value_states,
276
+ attention_mask=None,
277
+ scaling=self.scaling,
278
+ dropout=0.0,
279
+ is_causal=False,
280
+ **kwargs,
281
+ )
282
+ else:
283
+ splits = [torch.split(tensor, lengths, dim=2) for tensor in (query_states, key_states, value_states)]
284
+ attn_outputs, attn_weights = [], []
285
+ for q, k, v in zip(*splits):
286
+ output, weights = attention_interface(
287
+ self,
288
+ q,
289
+ k,
290
+ v,
291
+ attention_mask=None,
292
+ scaling=self.scaling,
293
+ dropout=0.0,
294
+ is_causal=False,
295
+ **kwargs,
296
+ )
297
+ attn_outputs.append(output)
298
+ attn_weights.append(weights)
299
+ attn_output = torch.cat(attn_outputs, dim=1)
300
+ # frames of different shapes have differently sized maps: one entry per frame, or None under sdpa
301
+ attn_weights = None if attn_weights[0] is None else tuple(attn_weights)
302
+
303
+ attn_output = attn_output.reshape(seq_length, -1).contiguous()
304
+ attn_output = self.out_proj(attn_output)
305
+ return attn_output, attn_weights
306
+
307
+
308
+ class Lfm3VlVisionConv2d(nn.Module):
309
+ """Gated depthwise 2D convolution over the patch grid of each frame."""
310
+
311
+ def __init__(self, config: Lfm3VlVisionConfig):
312
+ super().__init__()
313
+ self.spatial_merge_size = config.spatial_merge_size
314
+ self.in_proj = nn.Linear(config.hidden_size, 3 * config.hidden_size, bias=False)
315
+ self.spatial_conv = nn.Conv2d(
316
+ config.hidden_size,
317
+ config.hidden_size,
318
+ kernel_size=config.conv_kernel_size,
319
+ padding=config.conv_kernel_size // 2,
320
+ groups=config.hidden_size,
321
+ bias=False,
322
+ )
323
+ self.out_proj = nn.Linear(config.hidden_size, config.hidden_size, bias=False)
324
+
325
+ def forward(self, hidden_states: torch.Tensor, frame_shapes: list[list[int]]):
326
+ B, C, x = self.in_proj(hidden_states).chunk(3, dim=-1)
327
+ gated = B * x
328
+ merge = self.spatial_merge_size
329
+ num_frames = len(frame_shapes)
330
+ if len(set(map(tuple, frame_shapes))) == 1:
331
+ # frames of one shape (every video, equal-size images) convolve as one batch: merge-block order -> raster
332
+ # grid, convolve, back
333
+ height, width = frame_shapes[0]
334
+ frames = gated.view(num_frames, height // merge, width // merge, merge, merge, -1)
335
+ frames = frames.permute(0, 5, 1, 3, 2, 4).reshape(num_frames, -1, height, width)
336
+ frames = self.spatial_conv(frames)
337
+ frames = frames.view(num_frames, -1, height // merge, merge, width // merge, merge).permute(
338
+ 0, 2, 4, 3, 5, 1
339
+ )
340
+ conv_output = frames.reshape(gated.shape[0], -1)
341
+ else:
342
+ conv_outputs = []
343
+ lengths = [height * width for height, width in frame_shapes]
344
+ for frame, (height, width) in zip(torch.split(gated, lengths, dim=0), frame_shapes):
345
+ frame = frame.view(height // merge, width // merge, merge, merge, -1)
346
+ frame = frame.permute(4, 0, 2, 1, 3).reshape(1, -1, height, width)
347
+ frame = self.spatial_conv(frame)
348
+ frame = frame.view(-1, height // merge, merge, width // merge, merge).permute(1, 3, 2, 4, 0)
349
+ conv_outputs.append(frame.reshape(height * width, -1))
350
+ conv_output = torch.cat(conv_outputs, dim=0)
351
+ return self.out_proj(C * conv_output)
352
+
353
+
354
+ class Lfm3VlVisionLayer(GradientCheckpointingLayer):
355
+ def __init__(self, config: Lfm3VlVisionConfig, layer_idx: int):
356
+ super().__init__()
357
+ self.is_attention_layer = config.layer_types[layer_idx] == "full_attention"
358
+ if self.is_attention_layer:
359
+ self.self_attn = Lfm3VlVisionAttention(config)
360
+ else:
361
+ self.conv = Lfm3VlVisionConv2d(config)
362
+ self.feed_forward = Lfm3VlMLP(config)
363
+ self.operator_norm = Lfm3VlRMSNorm(config.hidden_size, eps=config.norm_eps)
364
+ self.ffn_norm = Lfm3VlRMSNorm(config.hidden_size, eps=config.norm_eps)
365
+
366
+ def forward(
367
+ self,
368
+ hidden_states: torch.Tensor,
369
+ cu_seqlens: torch.Tensor,
370
+ frame_shapes: list[list[int]],
371
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
372
+ **kwargs,
373
+ ) -> torch.Tensor:
374
+ if self.is_attention_layer:
375
+ operator_output, _ = self.self_attn(
376
+ self.operator_norm(hidden_states),
377
+ cu_seqlens=cu_seqlens,
378
+ position_embeddings=position_embeddings,
379
+ frame_shapes=frame_shapes,
380
+ **kwargs,
381
+ )
382
+ else:
383
+ operator_output = self.conv(self.operator_norm(hidden_states), frame_shapes)
384
+ hidden_states = hidden_states + operator_output
385
+ hidden_states = hidden_states + self.feed_forward(self.ffn_norm(hidden_states))
386
+ return hidden_states
387
+
388
+
389
+ def create_masks_by_layer_type(
390
+ config: PreTrainedConfig,
391
+ inputs_embeds: torch.Tensor,
392
+ attention_mask: torch.Tensor | None,
393
+ past_key_values: Cache | None,
394
+ position_ids: torch.Tensor | None = None,
395
+ ) -> dict:
396
+ """The cache sizes each mask from the layer it is asked about, and the generic mask utilities ask about the first
397
+ non-sliding layer, which for a leading `conv` layer redirects to the first attention layer of any type: name the
398
+ first layer of each mask's own type instead."""
399
+ config = config.get_text_config()
400
+ mask_kwargs = {
401
+ "config": config,
402
+ "inputs_embeds": inputs_embeds,
403
+ "attention_mask": attention_mask,
404
+ "past_key_values": past_key_values,
405
+ "position_ids": position_ids,
406
+ }
407
+ layer_types, causal_mask_mapping = config.layer_types, {}
408
+ if "full_attention" in layer_types:
409
+ layer_idx = layer_types.index("full_attention")
410
+ causal_mask_mapping["full_attention"] = create_causal_mask(**mask_kwargs, layer_idx=layer_idx)
411
+ if "sliding_attention" in layer_types:
412
+ layer_idx = layer_types.index("sliding_attention")
413
+ causal_mask_mapping["sliding_attention"] = create_sliding_window_causal_mask(
414
+ **mask_kwargs, layer_idx=layer_idx
415
+ )
416
+ if "conv" in layer_types:
417
+ causal_mask_mapping["conv"] = create_recurrent_attention_mask(**mask_kwargs)
418
+ return causal_mask_mapping
419
+
420
+
421
+ @auto_docstring
422
+ class Lfm3VlPreTrainedModel(PreTrainedModel):
423
+ config: Lfm3VlConfig
424
+ base_model_prefix = "model"
425
+ input_modalities = ("image", "video", "text")
426
+ supports_gradient_checkpointing = True
427
+ _skip_keys_device_placement = ["past_key_values"]
428
+
429
+ _supports_flash_attn = True
430
+ _supports_sdpa = True
431
+ # the cached decode step traces without graph breaks, like the text model
432
+ _can_compile_fullgraph = True
433
+ _supports_flex_attn = True
434
+ _supports_attention_backend = True
435
+ _no_split_modules = ["Lfm3VlVisionLayer"]
436
+
437
+ @staticmethod
438
+ def create_masks_for_generate(
439
+ config: PreTrainedConfig,
440
+ inputs_embeds: torch.Tensor,
441
+ attention_mask: torch.Tensor | None,
442
+ past_key_values: Cache | None,
443
+ position_ids: torch.Tensor | None = None,
444
+ **kwargs,
445
+ ) -> dict:
446
+ # the static-cache masks of the text model are sized by layer type, not by the generic first attention layer
447
+ return create_masks_by_layer_type(config, inputs_embeds, attention_mask, past_key_values, position_ids)
448
+
449
+
450
+ def get_vision_frame_shapes(grid_thw: torch.Tensor) -> list[list[int]]:
451
+ return grid_thw[:, 1:].repeat_interleave(grid_thw[:, 0], dim=0).tolist()
452
+
453
+
454
+ @auto_docstring(
455
+ custom_intro="""
456
+ The LFM3-VL vision encoder: a stack of gated convolution and bidirectional attention layers over the patches of
457
+ each image or video frame, embedded by a linear patchifier.
458
+ """
459
+ )
460
+ class Lfm3VlVisionModel(Lfm3VlPreTrainedModel):
461
+ config: Lfm3VlVisionConfig
462
+ input_modalities = ("image", "video")
463
+ _input_embed_layer = "patchifier"
464
+ _can_record_outputs = {
465
+ "hidden_states": Lfm3VlVisionLayer,
466
+ "attentions": OutputRecorder(Lfm3VlVisionAttention, index=1),
467
+ }
468
+
469
+ def __init__(self, config: Lfm3VlVisionConfig):
470
+ super().__init__(config)
471
+ self.spatial_merge_size = config.spatial_merge_size
472
+ patch_dim = config.in_channels * config.temporal_patch_size * config.patch_size**2
473
+ self.patchifier = nn.Linear(patch_dim, config.hidden_size, bias=False)
474
+ self.embedding_norm = Lfm3VlRMSNorm(config.hidden_size, eps=config.norm_eps)
475
+ self.rotary_pos_emb = Lfm3VlVisionRotaryEmbedding(config)
476
+ self.layers = nn.ModuleList(
477
+ [Lfm3VlVisionLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
478
+ )
479
+ self.gradient_checkpointing = False
480
+ self.post_init()
481
+
482
+ @merge_with_config_defaults
483
+ @capture_outputs
484
+ @auto_docstring
485
+ def forward(
486
+ self, pixel_values: torch.Tensor, grid_thw: torch.Tensor, **kwargs: Unpack[TransformersKwargs]
487
+ ) -> BaseModelOutputWithPooling:
488
+ r"""
489
+ pixel_values (`torch.Tensor` of shape `(num_patches, in_channels * temporal_patch_size * patch_size**2)`):
490
+ The flattened patches of every image or video, grouped by merge block.
491
+ grid_thw (`torch.LongTensor` of shape `(num_images_or_videos, 3)`):
492
+ The temporal, height and width dimensions of the patch grid of each image or video.
493
+ """
494
+ position_ids = get_vision_position_ids(grid_thw, self.spatial_merge_size, kwargs=kwargs)
495
+ cu_seqlens, max_seqlen = get_vision_attention_seqlens(grid_thw, self.config, kwargs=kwargs)
496
+ frame_shapes = get_vision_frame_shapes(grid_thw)
497
+
498
+ hidden_states = self.patchifier(pixel_values.to(self.patchifier.weight.dtype))
499
+ hidden_states = self.embedding_norm(hidden_states)
500
+ position_embeddings = self.rotary_pos_emb(hidden_states, position_ids)
501
+
502
+ for layer in self.layers:
503
+ hidden_states = layer(
504
+ hidden_states,
505
+ cu_seqlens=cu_seqlens,
506
+ frame_shapes=frame_shapes,
507
+ max_seqlen=max_seqlen,
508
+ position_embeddings=position_embeddings,
509
+ **kwargs,
510
+ )
511
+
512
+ return BaseModelOutputWithPooling(last_hidden_state=hidden_states)
513
+
514
+
515
+ class Lfm3VlMultiModalProjector(nn.Module):
516
+ """Merges each block of `downsample_factor**2` vision tokens into one language token."""
517
+
518
+ def __init__(self, config: Lfm3VlConfig):
519
+ super().__init__()
520
+ self.downsample_factor = config.downsample_factor
521
+ self.layer_norm = nn.LayerNorm(config.vision_config.hidden_size, eps=config.projector_layer_norm_eps)
522
+ self.linear_1 = nn.Linear(
523
+ config.vision_config.hidden_size * config.downsample_factor**2,
524
+ config.projector_hidden_size,
525
+ bias=config.projector_bias,
526
+ )
527
+ self.act = ACT2FN[config.projector_hidden_act]
528
+ self.linear_2 = nn.Linear(
529
+ config.projector_hidden_size, config.text_config.hidden_size, bias=config.projector_bias
530
+ )
531
+
532
+ def forward(self, image_features: torch.Tensor):
533
+ hidden_states = self.layer_norm(image_features).reshape(-1, self.linear_1.in_features)
534
+ hidden_states = self.linear_1(hidden_states)
535
+ hidden_states = self.act(hidden_states)
536
+ hidden_states = self.linear_2(hidden_states)
537
+ return hidden_states
538
+
539
+
540
+ @auto_docstring(
541
+ custom_intro="""
542
+ Base class for Lfm3Vl outputs, with hidden states and attentions.
543
+ """
544
+ )
545
+ @dataclass
546
+ class Lfm3VlModelOutputWithPast(BaseModelOutputWithPast):
547
+ r"""
548
+ past_key_values (`Cache`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
549
+ It is a [`~cache_utils.Cache`] instance. For more details, see our [kv cache guide](https://huggingface.co/docs/transformers/en/kv_cache).
550
+
551
+ Contains pre-computed hidden-states (key and values in the self-attention blocks) that can be used (see
552
+ `past_key_values` input) to speed up sequential decoding.
553
+ image_hidden_states (`torch.FloatTensor`, *optional*):
554
+ A `torch.FloatTensor` of size `(num_image_tokens, hidden_size)`.
555
+ image_hidden_states of the model produced by the vision encoder and after projecting the last hidden state.
556
+ video_hidden_states (`torch.FloatTensor`, *optional*):
557
+ A `torch.FloatTensor` of size `(num_video_tokens, hidden_size)`.
558
+ video_hidden_states of the model produced by the vision encoder and after projecting the last hidden state.
559
+ """
560
+
561
+ image_hidden_states: torch.FloatTensor | None = None
562
+ video_hidden_states: torch.FloatTensor | None = None
563
+
564
+
565
+ @auto_docstring(
566
+ custom_intro="""
567
+ Base class for Lfm3Vl causal language model (or autoregressive) outputs.
568
+ """
569
+ )
570
+ @dataclass
571
+ class Lfm3VlCausalLMOutputWithPast(CausalLMOutputWithPast):
572
+ r"""
573
+ image_hidden_states (`torch.FloatTensor`, *optional*):
574
+ A `torch.FloatTensor` of size `(num_image_tokens, hidden_size)`.
575
+ image_hidden_states of the model produced by the vision encoder and after projecting the last hidden state.
576
+ video_hidden_states (`torch.FloatTensor`, *optional*):
577
+ A `torch.FloatTensor` of size `(num_video_tokens, hidden_size)`.
578
+ video_hidden_states of the model produced by the vision encoder and after projecting the last hidden state.
579
+ """
580
+
581
+ image_hidden_states: torch.FloatTensor | None = None
582
+ video_hidden_states: torch.FloatTensor | None = None
583
+
584
+
585
+ @auto_docstring(
586
+ custom_intro="""
587
+ The Lfm3Vl model which consists of a vision backbone and a language model, without a language modeling head.
588
+ """
589
+ )
590
+ class Lfm3VlModel(Lfm3VlPreTrainedModel):
591
+ def __init__(self, config: Lfm3VlConfig):
592
+ super().__init__(config)
593
+ self.vision_tower = Lfm3VlVisionModel(config.vision_config)
594
+ self.multi_modal_projector = Lfm3VlMultiModalProjector(config)
595
+ self.language_model = Lfm3Model(config.text_config)
596
+ self.post_init()
597
+
598
+ @accepts_precomputed_kwargs(modality="image")
599
+ @can_return_tuple
600
+ @auto_docstring(
601
+ custom_intro="Obtains image last hidden states from the vision tower and apply multimodal projection."
602
+ )
603
+ def get_image_features(
604
+ self,
605
+ pixel_values: torch.FloatTensor,
606
+ image_grid_thw: torch.LongTensor,
607
+ **kwargs: Unpack[TransformersKwargs],
608
+ ) -> tuple | BaseModelOutputWithPooling:
609
+ r"""
610
+ pixel_values (`torch.FloatTensor` of shape `(num_patches, in_channels * temporal_patch_size * patch_size**2)`):
611
+ The flattened patches of every image, grouped by merge block.
612
+ image_grid_thw (`torch.LongTensor` of shape `(num_images, 3)`):
613
+ The temporal, height and width dimensions of the patch grid of each image.
614
+ """
615
+ image_outputs = self.vision_tower(pixel_values, grid_thw=image_grid_thw, return_dict=True, **kwargs)
616
+ image_features = self.multi_modal_projector(image_outputs.last_hidden_state)
617
+ split_sizes = (image_grid_thw.prod(-1) // self.vision_tower.spatial_merge_size**2).tolist()
618
+ image_outputs.pooler_output = torch.split(image_features, split_sizes)
619
+ return image_outputs
620
+
621
+ def get_placeholder_mask(
622
+ self,
623
+ input_ids: torch.LongTensor,
624
+ inputs_embeds: torch.FloatTensor,
625
+ image_features: torch.FloatTensor | None = None,
626
+ video_features: torch.FloatTensor | None = None,
627
+ ):
628
+ """Placeholder masks from `input_ids` or `inputs_embeds`, checked against the feature counts."""
629
+ if input_ids is None:
630
+ special_image_mask = inputs_embeds == self.get_input_embeddings()(
631
+ torch.full((), self.config.image_token_id, dtype=torch.long, device=inputs_embeds.device)
632
+ )
633
+ special_image_mask = special_image_mask.all(-1)
634
+ special_video_mask = inputs_embeds == self.get_input_embeddings()(
635
+ torch.full((), self.config.video_token_id, dtype=torch.long, device=inputs_embeds.device)
636
+ )
637
+ special_video_mask = special_video_mask.all(-1)
638
+ else:
639
+ special_image_mask = input_ids == self.config.image_token_id
640
+ special_video_mask = input_ids == self.config.video_token_id
641
+
642
+ n_image_tokens = special_image_mask.sum()
643
+ special_image_mask = special_image_mask.unsqueeze(-1).to(inputs_embeds.device)
644
+ if image_features is not None:
645
+ torch_compilable_check(
646
+ n_image_tokens * inputs_embeds.shape[-1] == image_features.numel(),
647
+ f"Image features and image tokens do not match, tokens: {n_image_tokens}, features: {image_features.shape[0]}",
648
+ )
649
+
650
+ n_video_tokens = special_video_mask.sum()
651
+ special_video_mask = special_video_mask.unsqueeze(-1).to(inputs_embeds.device)
652
+ if video_features is not None:
653
+ torch_compilable_check(
654
+ n_video_tokens * inputs_embeds.shape[-1] == video_features.numel(),
655
+ f"Video features and video tokens do not match, tokens: {n_video_tokens}, features: {video_features.shape[0]}",
656
+ )
657
+ return special_image_mask, special_video_mask
658
+
659
+ @can_return_tuple
660
+ @auto_docstring
661
+ def forward(
662
+ self,
663
+ input_ids: torch.LongTensor | None = None,
664
+ attention_mask: torch.Tensor | None = None,
665
+ position_ids: torch.LongTensor | None = None,
666
+ past_key_values: Cache | None = None,
667
+ inputs_embeds: torch.FloatTensor | None = None,
668
+ pixel_values: torch.FloatTensor | None = None,
669
+ image_grid_thw: torch.LongTensor | None = None,
670
+ pixel_values_videos: torch.FloatTensor | None = None,
671
+ video_grid_thw: torch.LongTensor | None = None,
672
+ use_cache: bool | None = None,
673
+ mm_encoder_outputs: dict[str, BaseModelOutputWithPooling] | None = None,
674
+ **kwargs: Unpack[TransformersKwargs],
675
+ ) -> tuple | Lfm3VlModelOutputWithPast:
676
+ r"""
677
+ pixel_values (`torch.FloatTensor` of shape `(num_patches, in_channels * temporal_patch_size * patch_size**2)`, *optional*):
678
+ The flattened patches of every image, grouped by merge block.
679
+ image_grid_thw (`torch.LongTensor` of shape `(num_images, 3)`, *optional*):
680
+ The temporal, height and width dimensions of the patch grid of each image.
681
+ pixel_values_videos (`torch.FloatTensor` of shape `(num_patches, in_channels * temporal_patch_size * patch_size**2)`, *optional*):
682
+ The flattened patches of every video, grouped by merge block.
683
+ video_grid_thw (`torch.LongTensor` of shape `(num_videos, 3)`, *optional*):
684
+ The temporal, height and width dimensions of the patch grid of each video.
685
+ """
686
+ if (input_ids is None) ^ (inputs_embeds is not None):
687
+ raise ValueError("You must specify exactly one of input_ids or inputs_embeds")
688
+
689
+ if (pixel_values is not None or pixel_values_videos is not None) and mm_encoder_outputs is not None:
690
+ raise ValueError(
691
+ "You cannot specify both pixel_values/pixel_values_videos and mm_encoder_outputs at the same time"
692
+ )
693
+
694
+ if inputs_embeds is None:
695
+ inputs_embeds = self.get_input_embeddings()(input_ids)
696
+
697
+ mm_encoder_outputs = mm_encoder_outputs if mm_encoder_outputs is not None else {}
698
+ if (pixel_values is None) != (image_grid_thw is None):
699
+ raise ValueError("pixel_values and image_grid_thw must be supplied together")
700
+ if (pixel_values_videos is None) != (video_grid_thw is None):
701
+ raise ValueError("pixel_values_videos and video_grid_thw must be supplied together")
702
+ if mm_encoder_outputs.get("image") is None and pixel_values is not None:
703
+ mm_encoder_outputs["image"] = self.get_image_features(
704
+ pixel_values, image_grid_thw, return_dict=True, **kwargs
705
+ )
706
+ if mm_encoder_outputs.get("video") is None and pixel_values_videos is not None:
707
+ mm_encoder_outputs["video"] = self.get_video_features(
708
+ pixel_values_videos, video_grid_thw, return_dict=True, **kwargs
709
+ )
710
+
711
+ image_features = None
712
+ if mm_encoder_outputs.get("image") is not None:
713
+ image_features = torch.cat(mm_encoder_outputs["image"].pooler_output, dim=0).to(
714
+ inputs_embeds.device, inputs_embeds.dtype
715
+ )
716
+ image_mask, _ = self.get_placeholder_mask(
717
+ input_ids, inputs_embeds=inputs_embeds, image_features=image_features
718
+ )
719
+ inputs_embeds = inputs_embeds.masked_scatter(image_mask, image_features)
720
+
721
+ video_features = None
722
+ if mm_encoder_outputs.get("video") is not None:
723
+ video_features = torch.cat(mm_encoder_outputs["video"].pooler_output, dim=0).to(
724
+ inputs_embeds.device, inputs_embeds.dtype
725
+ )
726
+ _, video_mask = self.get_placeholder_mask(
727
+ input_ids, inputs_embeds=inputs_embeds, video_features=video_features
728
+ )
729
+ inputs_embeds = inputs_embeds.masked_scatter(video_mask, video_features)
730
+
731
+ outputs = self.language_model(
732
+ attention_mask=attention_mask,
733
+ position_ids=position_ids,
734
+ past_key_values=past_key_values,
735
+ inputs_embeds=inputs_embeds,
736
+ use_cache=use_cache,
737
+ **kwargs,
738
+ )
739
+
740
+ return Lfm3VlModelOutputWithPast(
741
+ last_hidden_state=outputs.last_hidden_state,
742
+ past_key_values=outputs.past_key_values,
743
+ hidden_states=outputs.hidden_states,
744
+ attentions=outputs.attentions,
745
+ image_hidden_states=image_features,
746
+ video_hidden_states=video_features,
747
+ )
748
+
749
+ @accepts_precomputed_kwargs(modality="video")
750
+ @can_return_tuple
751
+ @auto_docstring(
752
+ custom_intro="Obtains video last hidden states from the vision tower and apply multimodal projection."
753
+ )
754
+ def get_video_features(
755
+ self,
756
+ pixel_values_videos: torch.FloatTensor,
757
+ video_grid_thw: torch.LongTensor,
758
+ **kwargs: Unpack[TransformersKwargs],
759
+ ) -> tuple | BaseModelOutputWithPooling:
760
+ r"""
761
+ pixel_values_videos (`torch.FloatTensor` of shape `(num_patches, in_channels * temporal_patch_size * patch_size**2)`):
762
+ The flattened patches of every video, grouped by merge block.
763
+ video_grid_thw (`torch.LongTensor` of shape `(num_videos, 3)`):
764
+ The temporal, height and width dimensions of the patch grid of each video.
765
+ """
766
+ return self.get_image_features(pixel_values_videos, video_grid_thw, **kwargs)
767
+
768
+
769
+ def reject_packed_generation(config: PreTrainedConfig, input_ids, generation_config, model_kwargs, is_first_iteration):
770
+ """`generate` decodes from the cache its prefill fills, and no mask it builds isolates packed documents
771
+ (`position_ids` restarting): the prompt is refused before the prefill, one forward or every chunk, writes to it."""
772
+ position_ids, attention_mask = model_kwargs.get("position_ids"), model_kwargs.get("attention_mask")
773
+ cache = model_kwargs.get("past_key_values")
774
+ if cache is None or position_ids is None or position_ids.ndim != 2:
775
+ return
776
+ if attention_mask is not None and attention_mask.ndim != 2:
777
+ return
778
+ has_attention = any(t != "conv" for t in config.get_text_config().layer_types)
779
+ filled = bool(cache.get_seq_length() > 0 if has_attention else cache.has_previous_state())
780
+ inputs_embeds = model_kwargs.get("inputs_embeds") if is_first_iteration else None
781
+ length = input_ids.shape[1] if inputs_embeds is None else inputs_embeds.shape[1]
782
+ if generation_config.prefill_chunk_size is not None:
783
+ # the chunks run from the start of the prompt
784
+ position_ids = position_ids[:, :length]
785
+ else:
786
+ if filled and has_attention:
787
+ # the slice `_prefill` feeds: a prompt that spans the cache is cut to its uncached part
788
+ mask_length = getattr(generation_config, "_mask_length", -1)
789
+ if attention_mask is not None:
790
+ mask_length = attention_mask.shape[1]
791
+ if inputs_embeds is not None or mask_length == length:
792
+ length -= int(cache.get_seq_length())
793
+ position_ids = position_ids[:, position_ids.shape[1] - length :]
794
+ if attention_mask is None:
795
+ attention_mask = torch.ones_like(position_ids)
796
+ if bool(packed_under_padding(position_ids, attention_mask, filled).any()):
797
+ raise ValueError(
798
+ "`generate` cannot decode packed documents (`position_ids` restarting, or starting at 0 on a cache that "
799
+ "holds tokens): attention would span them. Generate each document in its own row of a left-padded batch."
800
+ )
801
+
802
+
803
+ def packed_under_padding(position_ids: torch.Tensor, padding_mask: torch.Tensor, filled) -> torch.Tensor:
804
+ """Padding repeats position 0, so packing is a restart between two kept tokens, or a kept position 0 on a filled
805
+ cache whose row holds a kept token; attention under a padding mask cannot isolate it."""
806
+ restarts = torch.diff(position_ids, prepend=position_ids[:, :1] - 1, dim=-1) != 1
807
+ valid = padding_mask[:, -restarts.shape[-1] :].bool()
808
+ packed = restarts[:, 1:] & valid[:, 1:] & valid[:, :-1]
809
+ if filled:
810
+ # a row whose cached tokens are all padding (chunked prefill of a left-padded batch) has no document to pack
811
+ # against; a mask covering only the chunk keeps the strict rule
812
+ cached = padding_mask[:, : padding_mask.shape[-1] - restarts.shape[-1]]
813
+ cached_valid = cached.bool().any(-1, keepdim=True) if cached.shape[-1] else torch.ones_like(valid[:, :1])
814
+ packed = packed | (valid[:, :1] & (position_ids[:, :1] == 0) & cached_valid)
815
+ return packed
816
+
817
+
818
+ @auto_docstring(
819
+ custom_intro="""
820
+ The LFM3_VL model which consists of a vision backbone and a language model.
821
+ """
822
+ )
823
+ class Lfm3VlForConditionalGeneration(Lfm3VlPreTrainedModel, GenerationMixin):
824
+ _tied_weights_keys = {"lm_head.weight": "model.language_model.embed_tokens.weight"}
825
+
826
+ def __init__(self, config: Lfm3VlConfig):
827
+ super().__init__(config)
828
+ self.model = Lfm3VlModel(config)
829
+ self.lm_head = nn.Linear(config.text_config.hidden_size, config.text_config.vocab_size, bias=False)
830
+ self.post_init()
831
+
832
+ def get_output_embeddings(self) -> nn.Module:
833
+ return self.lm_head
834
+
835
+ @auto_docstring
836
+ def get_image_features(
837
+ self,
838
+ pixel_values: torch.FloatTensor,
839
+ image_grid_thw: torch.LongTensor,
840
+ **kwargs: Unpack[TransformersKwargs],
841
+ ) -> tuple | BaseModelOutputWithPooling:
842
+ r"""
843
+ pixel_values (`torch.FloatTensor` of shape `(num_patches, in_channels * temporal_patch_size * patch_size**2)`):
844
+ The flattened patches of every image, grouped by merge block.
845
+ image_grid_thw (`torch.LongTensor` of shape `(num_images, 3)`):
846
+ The temporal, height and width dimensions of the patch grid of each image.
847
+ """
848
+ return self.model.get_image_features(pixel_values, image_grid_thw, **kwargs)
849
+
850
+ @can_return_tuple
851
+ @auto_docstring
852
+ def forward(
853
+ self,
854
+ input_ids: torch.LongTensor | None = None,
855
+ attention_mask: torch.Tensor | None = None,
856
+ position_ids: torch.LongTensor | None = None,
857
+ past_key_values: Cache | None = None,
858
+ inputs_embeds: torch.FloatTensor | None = None,
859
+ pixel_values: torch.FloatTensor | None = None,
860
+ image_grid_thw: torch.LongTensor | None = None,
861
+ pixel_values_videos: torch.FloatTensor | None = None,
862
+ video_grid_thw: torch.LongTensor | None = None,
863
+ labels: torch.LongTensor | None = None,
864
+ use_cache: bool | None = None,
865
+ logits_to_keep: int | torch.Tensor = 0,
866
+ mm_encoder_outputs: dict[str, BaseModelOutputWithPooling] | None = None,
867
+ **kwargs: Unpack[TransformersKwargs],
868
+ ) -> tuple | Lfm3VlCausalLMOutputWithPast:
869
+ r"""
870
+ pixel_values (`torch.FloatTensor` of shape `(num_patches, in_channels * temporal_patch_size * patch_size**2)`, *optional*):
871
+ The flattened patches of every image, grouped by merge block.
872
+ image_grid_thw (`torch.LongTensor` of shape `(num_images, 3)`, *optional*):
873
+ The temporal, height and width dimensions of the patch grid of each image.
874
+ pixel_values_videos (`torch.FloatTensor` of shape `(num_patches, in_channels * temporal_patch_size * patch_size**2)`, *optional*):
875
+ The flattened patches of every video, grouped by merge block.
876
+ video_grid_thw (`torch.LongTensor` of shape `(num_videos, 3)`, *optional*):
877
+ The temporal, height and width dimensions of the patch grid of each video.
878
+ labels (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*):
879
+ Labels for computing the masked language modeling loss. Indices should either be in `[0, ...,
880
+ config.vocab_size]` or -100 (see `input_ids` docstring). Tokens with indices set to `-100` are ignored
881
+ (masked), the loss is only computed for the tokens with labels in `[0, ..., config.vocab_size]`.
882
+
883
+ Example:
884
+
885
+ ```python
886
+ >>> from transformers import AutoProcessor, AutoModelForImageTextToText
887
+
888
+ >>> model = AutoModelForImageTextToText.from_pretrained("LiquidAI/LFM3-VL-2B")
889
+ >>> processor = AutoProcessor.from_pretrained("LiquidAI/LFM3-VL-2B")
890
+
891
+ >>> conversation = [
892
+ ... {
893
+ ... "role": "user",
894
+ ... "content": [
895
+ ... {"type": "image", "url": "https://www.ilankelman.org/stopsigns/australia.jpg"},
896
+ ... {"type": "text", "text": "What is in this image?"},
897
+ ... ],
898
+ ... },
899
+ ... ]
900
+
901
+ >>> inputs = processor.apply_chat_template(
902
+ ... conversation, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt"
903
+ ... )
904
+
905
+ >>> outputs = model.generate(**inputs, max_new_tokens=45)
906
+ >>> processor.batch_decode(outputs, skip_special_tokens=True)[0]
907
+ ```"""
908
+ outputs = self.model(
909
+ input_ids=input_ids,
910
+ attention_mask=attention_mask,
911
+ position_ids=position_ids,
912
+ past_key_values=past_key_values,
913
+ inputs_embeds=inputs_embeds,
914
+ pixel_values=pixel_values,
915
+ image_grid_thw=image_grid_thw,
916
+ pixel_values_videos=pixel_values_videos,
917
+ video_grid_thw=video_grid_thw,
918
+ use_cache=use_cache,
919
+ mm_encoder_outputs=mm_encoder_outputs,
920
+ **kwargs,
921
+ )
922
+
923
+ hidden_states = outputs[0]
924
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
925
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
926
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
927
+
928
+ loss = None
929
+ if labels is not None:
930
+ loss = self.loss_function(
931
+ logits=logits, labels=labels, vocab_size=self.config.text_config.vocab_size, **kwargs
932
+ )
933
+
934
+ return Lfm3VlCausalLMOutputWithPast(
935
+ loss=loss,
936
+ logits=logits,
937
+ past_key_values=outputs.past_key_values,
938
+ hidden_states=outputs.hidden_states,
939
+ attentions=outputs.attentions,
940
+ image_hidden_states=outputs.image_hidden_states,
941
+ video_hidden_states=outputs.video_hidden_states,
942
+ )
943
+
944
+ def _prefill(self, input_ids, generation_config, model_kwargs, is_first_iteration=True):
945
+ # the text model's refusal, before the prefill writes to the cache
946
+ reject_packed_generation(self.config, input_ids, generation_config, model_kwargs, is_first_iteration)
947
+ return super()._prefill(input_ids, generation_config, model_kwargs, is_first_iteration=is_first_iteration)
948
+
949
+ @auto_docstring
950
+ def get_video_features(
951
+ self,
952
+ pixel_values_videos: torch.FloatTensor,
953
+ video_grid_thw: torch.LongTensor,
954
+ **kwargs: Unpack[TransformersKwargs],
955
+ ) -> tuple | BaseModelOutputWithPooling:
956
+ r"""
957
+ pixel_values_videos (`torch.FloatTensor` of shape `(num_patches, in_channels * temporal_patch_size * patch_size**2)`):
958
+ The flattened patches of every video, grouped by merge block.
959
+ video_grid_thw (`torch.LongTensor` of shape `(num_videos, 3)`):
960
+ The temporal, height and width dimensions of the patch grid of each video.
961
+ """
962
+ return self.model.get_video_features(pixel_values_videos, video_grid_thw, **kwargs)
963
+
964
+
965
+ __all__ = ["Lfm3VlForConditionalGeneration", "Lfm3VlModel", "Lfm3VlPreTrainedModel", "Lfm3VlVisionModel"]