modernbert.py 13.3 KB
Newer Older
xsank's avatar
xsank committed
1
# SPDX-License-Identifier: Apache-2.0
2
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
from collections.abc import Iterable, Set
4
from typing import Optional, Union
xsank's avatar
xsank committed
5
6
7
8
9

import torch
from torch import nn
from transformers import ModernBertConfig

10
from vllm.attention.layers.encoder_only_attention import EncoderOnlyAttention
11
from vllm.compilation.decorators import support_torch_compile
xsank's avatar
xsank committed
12
13
from vllm.config import VllmConfig
from vllm.distributed import get_tensor_model_parallel_world_size
14
15
16
17
18
19
20
21
22
from vllm.model_executor.layers.linear import QKVParallelLinear, RowParallelLinear
from vllm.model_executor.layers.pooler import (
    ClassifierPooler,
    DispatchPooler,
    Pooler,
    PoolingMethod,
    PoolingParamsUpdate,
    PoolingType,
)
xsank's avatar
xsank committed
23
from vllm.model_executor.layers.rotary_embedding import RotaryEmbedding
24
from vllm.model_executor.layers.vocab_parallel_embedding import VocabParallelEmbedding
xsank's avatar
xsank committed
25
from vllm.model_executor.model_loader.weight_utils import default_weight_loader
26
from vllm.sequence import IntermediateTensors
27
from vllm.tasks import PoolingTask
28
from vllm.v1.pool.metadata import PoolingMetadata
xsank's avatar
xsank committed
29

30
31
from .interfaces import SupportsCrossEncoding
from .interfaces_base import default_pooling_type
xsank's avatar
xsank committed
32
33
34
35
36
37
38
from .utils import WeightsMapper, maybe_prefix


class ModernBertEmbeddings(nn.Module):
    def __init__(self, config: ModernBertConfig):
        super().__init__()
        self.config = config
39
40
41
42
43
44
        self.tok_embeddings = VocabParallelEmbedding(
            config.vocab_size, config.hidden_size
        )
        self.norm = nn.LayerNorm(
            config.hidden_size, eps=config.layer_norm_eps, bias=config.norm_bias
        )
xsank's avatar
xsank committed
45

46
47
48
    def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
        return self.tok_embeddings(input_ids)

xsank's avatar
xsank committed
49
50
51
52
53
    def forward(
        self,
        input_ids: torch.Tensor,
        inputs_embeds: Optional[torch.Tensor] = None,
    ) -> torch.Tensor:
54
        if inputs_embeds is not None:
xsank's avatar
xsank committed
55
56
57
58
59
60
61
62
            return self.norm(inputs_embeds)
        else:
            inputs_embeds = self.tok_embeddings(input_ids)
            embeddings = self.norm(inputs_embeds)
            return embeddings


class ModernBertRotaryEmbedding(RotaryEmbedding):
63
    def __init__(self, config: ModernBertConfig, head_size: int, dim: int, base: float):
xsank's avatar
xsank committed
64
65
66
67
68
69
        super().__init__(
            head_size=head_size,
            rotary_dim=dim,
            max_position_embeddings=config.max_position_embeddings,
            base=base,
            is_neox_style=True,
70
71
            dtype=torch.float16,
        )
xsank's avatar
xsank committed
72
73
74
75
        self.config = config


class ModernBertAttention(nn.Module):
76
    def __init__(self, config: ModernBertConfig, layer_id: Optional[int] = None):
xsank's avatar
xsank committed
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
        super().__init__()
        self.config = config
        self.hidden_size = config.hidden_size
        tp_size = get_tensor_model_parallel_world_size()
        self.layer_id = layer_id
        self.deterministic_flash_attn = config.deterministic_flash_attn
        self.num_heads = config.num_attention_heads
        assert self.num_heads % tp_size == 0
        self.head_dim = config.hidden_size // config.num_attention_heads
        self.all_head_size = self.head_dim * self.num_heads
        self.scaling = self.head_dim**-0.5
        self.Wqkv = QKVParallelLinear(
            config.hidden_size,
            self.head_dim,
            self.num_heads,
            bias=config.attention_bias,
        )

95
        sliding_window = None
xsank's avatar
xsank committed
96
        if layer_id % config.global_attn_every_n_layers != 0:
97
            sliding_window = config.local_attention // 2
98
99
100
101
102
            rope_theta = (
                config.local_rope_theta
                if config.local_rope_theta is not None
                else config.global_rope_theta
            )
xsank's avatar
xsank committed
103
        else:
104
            rope_theta = config.global_rope_theta
xsank's avatar
xsank committed
105

106
107
108
        self.rotary_emb = ModernBertRotaryEmbedding(
            config=config, head_size=self.head_dim, dim=self.head_dim, base=rope_theta
        )
109
110
111
112
113
        self.attn = EncoderOnlyAttention(
            self.num_heads,
            self.head_dim,
            self.scaling,
            prefix=f"{layer_id}.attn",
114
115
116
117
118
            per_layer_sliding_window=sliding_window,
        )
        self.Wo = RowParallelLinear(
            config.hidden_size, config.hidden_size, bias=config.attention_bias
        )
xsank's avatar
xsank committed
119
120
121
122

    def forward(
        self,
        hidden_states: torch.Tensor,
123
        position_ids: torch.Tensor,
xsank's avatar
xsank committed
124
125
126
127
128
129
130
131
132
133
134
135
136
137
    ) -> torch.Tensor:
        qkv, _ = self.Wqkv(hidden_states)
        q, k, v = qkv.split([self.all_head_size] * 3, dim=-1)
        q, k = self.rotary_emb(position_ids, q, k)
        attn_outputs = self.attn(q, k, v)
        hidden_states = attn_outputs
        hidden_states, _ = self.Wo(hidden_states)
        return hidden_states


class ModernBertMLP(nn.Module):
    def __init__(self, config: ModernBertConfig):
        super().__init__()
        self.config = config
138
139
140
        self.Wi = nn.Linear(
            config.hidden_size, int(config.intermediate_size) * 2, bias=config.mlp_bias
        )
xsank's avatar
xsank committed
141
        self.act = nn.GELU()
142
143
144
        self.Wo = RowParallelLinear(
            config.intermediate_size, config.hidden_size, bias=config.mlp_bias
        )
xsank's avatar
xsank committed
145
146
147
148
149
150
151

    def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
        input, gate = self.Wi(hidden_states).chunk(2, dim=-1)
        return self.Wo(self.act(input) * gate)[0]


class ModernBertLayer(nn.Module):
152
153
154
    def __init__(
        self, config: ModernBertConfig, prefix: str = "", layer_id: Optional[int] = None
    ):
xsank's avatar
xsank committed
155
156
157
158
159
        super().__init__()
        self.config = config
        if layer_id == 0:
            self.attn_norm = nn.Identity()
        else:
160
161
162
            self.attn_norm = nn.LayerNorm(
                config.hidden_size, eps=config.norm_eps, bias=config.norm_bias
            )
xsank's avatar
xsank committed
163
        self.attn = ModernBertAttention(config=config, layer_id=layer_id)
164
165
166
        self.mlp_norm = nn.LayerNorm(
            config.hidden_size, eps=config.norm_eps, bias=config.norm_bias
        )
xsank's avatar
xsank committed
167
168
169
170
171
        self.mlp = ModernBertMLP(config)

    def forward(
        self,
        hidden_states: torch.Tensor,
172
173
        position_ids: torch.Tensor,
    ) -> torch.Tensor:
174
175
176
        attn_outputs = self.attn(
            hidden_states=self.attn_norm(hidden_states), position_ids=position_ids
        )
xsank's avatar
xsank committed
177
178
179
180
181
182
183
184
185
186
        hidden_states = hidden_states + attn_outputs
        mlp_output = self.mlp(self.mlp_norm(hidden_states))
        hidden_states = hidden_states + mlp_output
        return hidden_states


class ModernBertEncoderLayer(nn.Module):
    def __init__(self, vllm_config: VllmConfig, prefix: str = ""):
        super().__init__()
        config = vllm_config.model_config.hf_config
187
188
189
190
191
192
        self.layers = nn.ModuleList(
            [
                ModernBertLayer(config=config, layer_id=layer_id)
                for layer_id in range(config.num_hidden_layers)
            ]
        )
xsank's avatar
xsank committed
193
194
195
196

    def forward(
        self,
        hidden_states: torch.Tensor,
197
        position_ids: torch.Tensor,
xsank's avatar
xsank committed
198
199
200
201
202
203
    ) -> torch.Tensor:
        for i, layer in enumerate(self.layers):
            hidden_states = layer(hidden_states, position_ids)
        return hidden_states


204
@support_torch_compile
205
@default_pooling_type("CLS")
xsank's avatar
xsank committed
206
207
class ModernBertModel(nn.Module):
    hf_to_vllm_mapper = WeightsMapper(
208
209
        orig_to_new_prefix={"layers.": "encoder_layer.layers."}
    )
xsank's avatar
xsank committed
210
211
212
213
214
215
216
217
218
219
220

    def __init__(
        self,
        vllm_config: VllmConfig,
        prefix: str = "",
    ):
        super().__init__()
        config = vllm_config.model_config.hf_config
        self.config = config
        self.embeddings = ModernBertEmbeddings(config)
        self.encoder_layer = ModernBertEncoderLayer(vllm_config)
221
222
223
        self.final_norm = nn.LayerNorm(
            config.hidden_size, eps=config.norm_eps, bias=config.norm_bias
        )
xsank's avatar
xsank committed
224

225
226
227
    def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
        return self.embeddings.get_input_embeddings(input_ids)

228
    def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
xsank's avatar
xsank committed
229
230
        weights = self.hf_to_vllm_mapper.apply(weights)
        params_dict = dict(self.named_parameters())
231
        loaded_params: set[str] = set()
xsank's avatar
xsank committed
232
233
234
235
        for name, loaded_weight in weights:
            if name.endswith(".bias") and name not in params_dict:
                continue
            param = params_dict[name]
236
            weight_loader = getattr(param, "weight_loader", default_weight_loader)
xsank's avatar
xsank committed
237
238
239
240
241
242
            weight_loader(param, loaded_weight)
            loaded_params.add(name)
        return loaded_params

    def forward(
        self,
243
244
        input_ids: torch.Tensor,
        positions: torch.Tensor,
245
        intermediate_tensors: Optional[IntermediateTensors] = None,
xsank's avatar
xsank committed
246
247
248
249
250
        inputs_embeds: Optional[torch.Tensor] = None,
    ) -> torch.Tensor:
        if inputs_embeds is not None:
            hidden_states = inputs_embeds
        else:
251
252
253
            hidden_states = self.embeddings(
                input_ids=input_ids, inputs_embeds=inputs_embeds
            )
xsank's avatar
xsank committed
254
255
256

        outputs = self.encoder_layer(
            hidden_states=hidden_states,
257
            position_ids=positions,
xsank's avatar
xsank committed
258
259
260
261
262
        )
        norm_outputs = self.final_norm(outputs)
        return norm_outputs


263
class ModernBertPooler(Pooler):
xsank's avatar
xsank committed
264
265
    def __init__(self, config: ModernBertConfig):
        super().__init__()
266
267
268

        pooling_type = PoolingType[config.classifier_pooling.upper()]
        self.pooling = PoolingMethod.from_pooling_type(pooling_type)
269
270
271
        self.dense = nn.Linear(
            config.hidden_size, config.hidden_size, config.classifier_bias
        )
xsank's avatar
xsank committed
272
        self.act = nn.GELU()
273
274
275
        self.norm = nn.LayerNorm(
            config.hidden_size, eps=config.norm_eps, bias=config.norm_bias
        )
xsank's avatar
xsank committed
276

277
278
279
280
    def get_supported_tasks(self) -> Set[PoolingTask]:
        return self.pooling.get_supported_tasks()

    def get_pooling_updates(self, task: PoolingTask) -> PoolingParamsUpdate:
281
        return self.pooling.get_pooling_updates(task)
282

283
    def _head(self, pooled_output: torch.Tensor):
284
        pooled_output = pooled_output.to(self.dense.weight.dtype)
285
286
        return self.norm(self.act(self.dense(pooled_output)))

287
288
289
290
291
292
    def forward(
        self,
        hidden_states: Union[torch.Tensor, list[torch.Tensor]],
        pooling_metadata: PoolingMetadata,
    ) -> Union[torch.Tensor, list[torch.Tensor]]:
        pooled_output = self.pooling(hidden_states, pooling_metadata)
293
294
295
296
297
298

        if isinstance(pooled_output, list):
            pooled_output = [self._head(output) for output in pooled_output]
        else:
            pooled_output = self._head(pooled_output)

xsank's avatar
xsank committed
299
300
301
        return pooled_output


302
@default_pooling_type("CLS")
303
class ModernBertForSequenceClassification(nn.Module, SupportsCrossEncoding):
304
305
    is_pooling_model = True

xsank's avatar
xsank committed
306
307
308
309
    def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
        super().__init__()
        config = vllm_config.model_config.hf_config
        self.config = config
310
311
312
313
314
315
316
317
        self.model = ModernBertModel(
            vllm_config=vllm_config, prefix=maybe_prefix(prefix, "modernbert")
        )
        self.classifier = nn.Linear(
            config.hidden_size,
            config.num_labels,
            dtype=vllm_config.model_config.head_dtype,
        )
318
        self.pooling = ModernBertPooler(config)
319
320
321
322

        pooler_config = vllm_config.model_config.pooler_config
        assert pooler_config is not None

323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
        self.pooler = DispatchPooler(
            {
                "encode": Pooler.for_encode(pooler_config),
                "classify": ClassifierPooler(
                    pooling=self.pooling,
                    classifier=self.classifier,
                    act_fn=ClassifierPooler.act_fn_for_seq_cls(
                        vllm_config.model_config
                    ),
                ),
                "score": ClassifierPooler(
                    pooling=self.pooling,
                    classifier=self.classifier,
                    act_fn=ClassifierPooler.act_fn_for_cross_encoder(
                        vllm_config.model_config
                    ),
                ),
            }
        )
xsank's avatar
xsank committed
342

343
344
345
    def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
        return self.model.get_input_embeddings(input_ids)

346
    def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]):
xsank's avatar
xsank committed
347
348
349
350
351
        self_weights = []

        def weight_filter():
            for name, weight in weights:
                if name.startswith("model."):
352
                    yield name[len("model.") :], weight
xsank's avatar
xsank committed
353
354
355
356
357
358
359
360
361
362
                else:
                    self_weights.append((name, weight))

        self.model.load_weights(weight_filter())

        params_dict = dict(self.named_parameters())

        for name, loaded_weight in self_weights:
            if name.startswith("classifier"):
                param = params_dict[name]
363
                weight_loader = getattr(param, "weight_loader", default_weight_loader)
xsank's avatar
xsank committed
364
365
                weight_loader(param, loaded_weight)
            if name.startswith("head"):
366
367
                param = params_dict["pooling." + name[len("head") + 1 :]]
                weight_loader = getattr(param, "weight_loader", default_weight_loader)
xsank's avatar
xsank committed
368
369
370
371
372
373
374
375
376
377
378
379
                weight_loader(param, loaded_weight)

    def forward(
        self,
        input_ids: Optional[torch.LongTensor],
        positions: torch.Tensor,
        intermediate_tensors: Optional[IntermediateTensors] = None,
        inputs_embeds: Optional[torch.Tensor] = None,
    ) -> torch.Tensor:
        return self.model(
            input_ids=input_ids,
            inputs_embeds=inputs_embeds,
380
            positions=positions,
xsank's avatar
xsank committed
381
        )