A high-throughput and memory-efficient inference and serving engine for LLMs
Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git
Synced Nov 15, 21:00 UTC
[Model] New model support for Phi-4-multimodal-instruct (#14119)
Congcong Chen committed
Mar 4, 2025 at 20:57 UTC
0a995d543464582522edcc88f8bf70af715c0129
10 files changed
+7159
-3
docs/source/models/supported_models.md
+8
-1
@@ -410,7 +410,7 @@ See [this page](#generative-models) for more information on how to use generativ
410
* ✅︎
411
- * `Phi3ForCausalLM`
412
* Phi-4, Phi-3
413
- * `microsoft/Phi-4`, `microsoft/Phi-3-mini-4k-instruct`, `microsoft/Phi-3-mini-128k-instruct`, `microsoft/Phi-3-medium-128k-instruct`, etc.
413
+ * `microsoft/Phi-4-mini-instruct`, `microsoft/Phi-4`, `microsoft/Phi-3-mini-4k-instruct`, `microsoft/Phi-3-mini-128k-instruct`, `microsoft/Phi-3-medium-128k-instruct`, etc.
414
* ✅︎
415
* ✅︎
416
- * `Phi3SmallForCausalLM`
@@ -856,6 +856,13 @@ See [this page](#generative-models) for more information on how to use generativ
856
*
857
* ✅︎
858
* ✅︎
859
+- * `Phi4MMForCausalLM`
860
+ * Phi-4-multimodal
861
+ * T + I<sup>+</sup> / T + A<sup>+</sup> / I<sup>+</sup> + A<sup>+</sup>
862
+ * `microsoft/Phi-4-multimodal-instruct`, etc.
863
+ * ✅︎
864
+ *
865
+ *
866
- * `PixtralForConditionalGeneration`
867
* Pixtral
868
* T + I<sup>+</sup>
requirements-common.txt
+1
@@ -37,3 +37,4 @@ depyf==0.18.0 # required for profiling and debugging with compilation config
37
cloudpickle # allows pickling lambda functions in model_executor/models/registry.py
38
watchfiles # required for http server to monitor the updates of TLS files
39
python-json-logger # Used by logging as per examples/other/logging_configuration.md
40
+scipy # Required for phi-4-multimodal-instruct
\ No newline at end of file
tests/models/registry.py
+2
@@ -272,6 +272,8 @@ _MULTIMODAL_EXAMPLE_MODELS = {
272
extras={"v2": "google/paligemma2-3b-ft-docci-448"}), # noqa: E501
273
"Phi3VForCausalLM": _HfExamplesInfo("microsoft/Phi-3-vision-128k-instruct",
274
trust_remote_code=True),
275
+ "Phi4MMForCausalLM": _HfExamplesInfo("microsoft/Phi-4-multimodal-instruct",
276
+ trust_remote_code=True),
277
"PixtralForConditionalGeneration": _HfExamplesInfo("mistralai/Pixtral-12B-2409", # noqa: E501
278
tokenizer_mode="mistral"),
279
"QwenVLForConditionalGeneration": _HfExamplesInfo("Qwen/Qwen-VL",
vllm/config.py
+2
-2
@@ -2284,9 +2284,9 @@ class LoRAConfig:
2284
return hash_str
2285
2286
def __post_init__(self):
2287
- # Setting the maximum rank to 256 should be able to satisfy the vast
2287
+ # Setting the maximum rank to 512 should be able to satisfy the vast
2288
# majority of applications.
2289
- possible_max_ranks = (8, 16, 32, 64, 128, 256)
2289
+ possible_max_ranks = (8, 16, 32, 64, 128, 256, 320, 512)
2290
possible_lora_extra_vocab_size = (0, 256, 512)
2291
if self.max_lora_rank not in possible_max_ranks:
2292
raise ValueError(
vllm/entrypoints/chat_utils.py
+4
@@ -395,6 +395,8 @@ class BaseMultiModalItemTracker(ABC, Generic[_T]):
395
if model_type == "phi3_v":
396
# Workaround since this token is not defined in the tokenizer
397
return f"<|image_{current_count}|>"
398
+ if model_type == "phi4mm":
399
+ return "<|endoftext10|>" # 200010 (see vocab.json in hf model)
400
if model_type in ("minicpmo", "minicpmv"):
401
return "(<image>./</image>)"
402
if model_type in ("blip-2", "chatglm", "fuyu", "paligemma",
@@ -424,6 +426,8 @@ class BaseMultiModalItemTracker(ABC, Generic[_T]):
426
elif modality == "audio":
427
if model_type == "ultravox":
428
return "<|audio|>"
429
+ if model_type == "phi4mm":
430
+ return "<|endoftext11|>" # 200011 (see vocab.json in hf model)
431
if model_type == "qwen2_audio":
432
return (f"Audio {current_count}: "
433
f"<|audio_bos|><|AUDIO|><|audio_eos|>")
vllm/model_executor/models/phi4mm.py
new
+1803
@@ -0,0 +1,1803 @@
1
+# SPDX-License-Identifier: Apache-2.0
2
+import math
3
+import re
4
+from functools import lru_cache
5
+from typing import (Dict, Iterable, List, Literal, Mapping, Optional, Tuple,
6
+ TypedDict, Union)
7
+
8
+import numpy as np
9
+import scipy.signal
10
+import torch
11
+import torch.nn as nn
12
+import torchvision.transforms as T
13
+from PIL import Image
14
+from transformers import PretrainedConfig
15
+from transformers.utils import logging
16
+
17
+from vllm.config import VllmConfig
18
+from vllm.distributed import get_pp_group, get_tensor_model_parallel_world_size
19
+from vllm.inputs import (INPUT_REGISTRY, DecoderOnlyInputs, DummyData,
20
+ InputContext)
21
+from vllm.inputs.data import TokenInputs, token_inputs
22
+from vllm.model_executor.layers.logits_processor import LogitsProcessor
23
+from vllm.model_executor.layers.quantization import QuantizationConfig
24
+from vllm.model_executor.layers.sampler import Sampler, SamplerOutput
25
+from vllm.model_executor.layers.vocab_parallel_embedding import (
26
+ DEFAULT_VOCAB_PADDING_SIZE, ParallelLMHead)
27
+from vllm.model_executor.models.llama import LlamaModel
28
+from vllm.model_executor.sampling_metadata import SamplingMetadata
29
+from vllm.multimodal import MULTIMODAL_REGISTRY
30
+from vllm.multimodal.inputs import MultiModalInputs, NestedTensors
31
+from vllm.sequence import IntermediateTensors, SequenceData
32
+from vllm.transformers_utils.tokenizer import cached_tokenizer_from_config
33
+
34
+from .interfaces import SupportsLoRA, SupportsMultiModal
35
+from .phi4mm_audio import AudioEmbedding
36
+from .utils import maybe_prefix
37
+from .vision_siglip_navit import get_siglip_vision_model
38
+
39
+# <|endoftext10|> (see vocab.json in hf model)
40
+_IMAGE_PLACEHOLDER_TOKEN_ID = 200010
41
+# <|endoftext11|>
42
+_AUDIO_PLACEHOLDER_TOKEN_ID = 200011
43
+
44
+_AUDIO_MAX_SOUNDFILE_SIZE = 241_000
45
+DUMMY_SAMPLING_FREQUENCY = 16_000 # kHz
46
+
47
+DYNAMIC_HD = 16
48
+AUDIO_TOKEN_PATTERN = r"<\|audio_(\d+)\|>"
49
+IMAGE_TOKEN_PATTERN = r"<\|image_(\d+)\|>"
50
+
51
+SIGLIP_NAME = "siglip-so400m-patch14-448"
52
+VISION_ENCODER_TO_PROCESSING_CONFIG = {
53
+ 'siglip-so400m-patch14-448': {
54
+ 'dynamic_hd': 16,
55
+ 'vit_image_size': 448,
56
+ 'vit_patch_size': 14,
57
+ 'token_compression_factor': 2,
58
+ },
59
+}
60
+logger = logging.get_logger(__name__)
61
+# This is a workaround to prevent text (user input) + audio + image
62
+# from being used in the same prompt.
63
+# It includes token ids for "/n" and tokens in added_tokens_decoder
64
+# from the tokenizer_confg.json file.
65
+NON_USER_INPUT_TOKENS = {
66
+ 198, 200010, 200011, 199999, 200018, 200019, 200020, 200021, 200022,
67
+ 200023, 200024, 200025, 200026, 200027, 200028
68
+}
69
+
70
+
71
+def get_max_dummy_image(ctx: InputContext):
72
+ hf_config = ctx.get_hf_config()
73
+ vision_encoder_name = hf_config.img_processor
74
+ if vision_encoder_name is None:
75
+ vision_encoder_name = SIGLIP_NAME
76
+ prepro_config = VISION_ENCODER_TO_PROCESSING_CONFIG[vision_encoder_name]
77
+ dynamic_hd_size = prepro_config['dynamic_hd']
78
+ vit_image_size = prepro_config['vit_image_size']
79
+
80
+ max_side = vit_image_size * dynamic_hd_size
81
+ dummy_image = dummy_image_for_phi4mm(vit_image_size, max_side)
82
+ return dummy_image
83
+
84
+
85
+# image token length
86
+def get_max_phi4mm_image_tokens(ctx: InputContext):
87
+ dummy_image = get_max_dummy_image(ctx)
88
+
89
+ hf_config = ctx.get_hf_config()
90
+ vision_encoder_name = hf_config.img_processor
91
+ if vision_encoder_name is None:
92
+ vision_encoder_name = SIGLIP_NAME
93
+ prepro_config = VISION_ENCODER_TO_PROCESSING_CONFIG[vision_encoder_name]
94
+ dynamic_hd_size = prepro_config['dynamic_hd']
95
+ vit_image_size = prepro_config['vit_image_size']
96
+ vit_patch_size = prepro_config['vit_patch_size']
97
+ token_compression_factor = prepro_config['token_compression_factor']
98
+
99
+ image_num_tokens = _compute_num_image_tokens(dummy_image, dynamic_hd_size,
100
+ vit_image_size,
101
+ vit_patch_size,
102
+ token_compression_factor)
103
+ return image_num_tokens
104
+
105
+
106
+def find_closest_aspect_ratio(aspect_ratio, target_ratios, width, height,
107
+ image_size):
108
+ best_ratio_diff = float('inf')
109
+ best_ratio = (1, 1)
110
+ area = width * height
111
+ for ratio in target_ratios:
112
+ target_aspect_ratio = ratio[0] / ratio[1]
113
+ ratio_diff = abs(aspect_ratio - target_aspect_ratio)
114
+ if ratio_diff < best_ratio_diff:
115
+ best_ratio_diff = ratio_diff
116
+ best_ratio = ratio
117
+ elif ratio_diff == best_ratio_diff:
118
+ if area > 0.5 * image_size * image_size * ratio[0] * ratio[1]:
119
+ best_ratio = ratio
120
+ return best_ratio
121
+
122
+
123
+def _find_target_aspect_ratio(image, image_size, max_num, min_num):
124
+ orig_width, orig_height = image.size
125
+
126
+ w_crop_num = math.ceil(orig_width / float(image_size))
127
+ h_crop_num = math.ceil(orig_height / float(image_size))
128
+ if w_crop_num * h_crop_num > max_num:
129
+ aspect_ratio = orig_width / orig_height
130
+
131
+ # calculate the existing image aspect ratio
132
+ target_ratios = set((i, j) for i in range(1, max_num + 1)
133
+ for j in range(1, max_num + 1)
134
+ if i * j <= max_num and i * j >= min_num)
135
+ target_ratios = sorted(target_ratios, key=lambda x: x[0] * x[1])
136
+
137
+ # find the closest aspect ratio to the target
138
+ target_aspect_ratio = find_closest_aspect_ratio(
139
+ aspect_ratio, target_ratios, orig_width, orig_height, image_size)
140
+
141
+ # calculate the target width and height
142
+ target_width = image_size * target_aspect_ratio[0]
143
+ target_height = image_size * target_aspect_ratio[1]
144
+ logger.debug("target_aspect_ratio: %s", target_aspect_ratio)
145
+ else:
146
+ target_width = image_size * w_crop_num
147
+ target_height = image_size * h_crop_num
148
+ target_aspect_ratio = (w_crop_num, h_crop_num)
149
+ return target_aspect_ratio, target_height, target_width
150
+
151
+
152
+def _get_padding_size(image, target_height, target_width):
153
+ orig_width, orig_height = image.size
154
+ ratio_width = target_width / orig_width
155
+ ratio_height = target_height / orig_height
156
+
157
+ if ratio_width < ratio_height:
158
+ padding_width = 0
159
+ padding_height = target_height - int(orig_height * ratio_width)
160
+ else:
161
+ padding_width = target_width - int(orig_width * ratio_height)
162
+ padding_height = 0
163
+ return padding_height, padding_width
164
+
165
+
166
+def dynamic_preprocess(image,
167
+ min_num=1,
168
+ max_num=12,
169
+ image_size=384,
170
+ mask_size=27):
171
+ target_aspect_ratio, target_height, target_width =\
172
+ _find_target_aspect_ratio(
173
+ image, image_size, max_num, min_num)
174
+ padding_height, padding_width = _get_padding_size(image, target_height,
175
+ target_width)
176
+
177
+ # Calculate the ratio
178
+ orig_width, orig_height = image.size
179
+ ratio_width = target_width / orig_width
180
+ ratio_height = target_height / orig_height
181
+ if ratio_width < ratio_height:
182
+ new_size = (target_width, int(orig_height * ratio_width))
183
+ else:
184
+ new_size = (int(orig_width * ratio_height), target_height)
185
+
186
+ attention_mask = torch.ones((int(mask_size * target_aspect_ratio[1]),
187
+ int(mask_size * target_aspect_ratio[0])))
188
+ if padding_width >= 14:
189
+ attention_mask[:, -math.floor(padding_width / 14):] = 0
190
+ if padding_height >= 14:
191
+ attention_mask[-math.floor(padding_height / 14):, :] = 0
192
+ assert attention_mask.sum(
193
+ ) > 0, f'attention mask is empty {attention_mask}'
194
+
195
+ if min(new_size[1], target_height) < 10 or min(new_size[0],
196
+ target_width) < 10:
197
+ raise ValueError(f'the aspect ratio is very extreme {new_size}')
198
+
199
+ image = T.functional.resize(
200
+ image,
201
+ [new_size[1], new_size[0]],
202
+ )
203
+
204
+ resized_img = T.functional.pad(image,
205
+ [0, 0, padding_width, padding_height],
206
+ fill=[255, 255, 255])
207
+
208
+ return resized_img, attention_mask
209
+
210
+
211
+def pad_to_max_num_crops(images, max_crops=5):
212
+ """
213
+ images: B x 3 x H x W, B<=max_crops
214
+ """
215
+ B, _, H, W = images.shape
216
+ if max_crops > B:
217
+ pad = torch.zeros(max_crops - B,
218
+ 3,
219
+ H,
220
+ W,
221
+ dtype=images.dtype,
222
+ device=images.device)
223
+ images = torch.cat([images, pad], dim=0)
224
+ return images
225
+
226
+
227
+def pad_mask_to_max_num_crops(masks, max_crops=5):
228
+ B, H, W = masks.shape
229
+ if max_crops > B:
230
+ pad = torch.ones(max_crops - B,
231
+ H,
232
+ W,
233
+ dtype=masks.dtype,
234
+ device=masks.device)
235
+ masks = torch.cat([masks, pad], dim=0)
236
+ return masks
237
+
238
+
239
+def preprocess(images, dynamic_hd_size, vit_resolution, vit_patch_size):
240
+
241
+ # Basic settings.
242
+ img_processor = T.Compose([
243
+ T.ToTensor(),
244
+ T.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),
245
+ ])
246
+ # Dynamic HD
247
+ base_resolution = vit_resolution
248
+ images = [image.convert('RGB') for image in images]
249
+ # cover 384 and 448 resolution
250
+ mask_resolution = base_resolution // vit_patch_size
251
+ elems, image_attention_masks = [], []
252
+ for im in images:
253
+ elem, attention_mask = dynamic_preprocess(im,
254
+ max_num=dynamic_hd_size,
255
+ image_size=base_resolution,
256
+ mask_size=mask_resolution)
257
+ elems.append(elem)
258
+ image_attention_masks.append(attention_mask)
259
+ hd_images = [img_processor(im) for im in elems]
260
+ global_image = [
261
+ torch.nn.functional.interpolate(
262
+ im.unsqueeze(0).float(),
263
+ size=(base_resolution, base_resolution),
264
+ mode='bicubic',
265
+ ).to(im.dtype) for im in hd_images
266
+ ]
267
+ shapes = [[im.size(1), im.size(2)] for im in hd_images]
268
+ mask_shapes = [[mask.size(0), mask.size(1)]
269
+ for mask in image_attention_masks]
270
+ global_attention_mask = [
271
+ torch.ones((1, mask_resolution, mask_resolution)) for _ in hd_images
272
+ ]
273
+ hd_images_reshape = [
274
+ im.reshape(1, 3, h // base_resolution, base_resolution,
275
+ w // base_resolution, base_resolution).permute(
276
+ 0, 2, 4, 1, 3, 5).reshape(-1, 3, base_resolution,
277
+ base_resolution).contiguous()
278
+ for im, (h, w) in zip(hd_images, shapes)
279
+ ]
280
+ attention_masks_reshape = [
281
+ mask.reshape(1, h // mask_resolution, mask_resolution,
282
+ w // mask_resolution, mask_resolution).permute(
283
+ 0, 1, 3, 2, 4).reshape(-1, mask_resolution,
284
+ mask_resolution).contiguous()
285
+ for mask, (h, w) in zip(image_attention_masks, mask_shapes)
286
+ ]
287
+ # NOTE token compression is hard coded here, and odd numbers seems to fail
288
+ downsample_attention_masks = [
289
+ mask[:, 0::2,
290
+ 0::2].reshape(1, h // mask_resolution, w // mask_resolution,
291
+ mask_resolution // 2 + mask_resolution % 2,
292
+ mask_resolution // 2 + mask_resolution % 2).permute(
293
+ 0, 1, 3, 2, 4)
294
+ for mask, (h, w) in zip(attention_masks_reshape, mask_shapes)
295
+ ]
296
+ downsample_attention_masks = [
297
+ mask.reshape(mask.size(1) * mask.size(2),
298
+ mask.size(3) * mask.size(4))
299
+ for mask in downsample_attention_masks
300
+ ]
301
+ # NOTE hard coded number of tokens
302
+ num_img_tokens = [
303
+ 256 + 1 + int(mask.sum().item()) + int(mask[:, 0].sum().item()) + 16
304
+ for mask in downsample_attention_masks
305
+ ]
306
+
307
+ hd_images_reshape = [
308
+ torch.cat([_global_image] + [_im], dim=0)
309
+ for _global_image, _im in zip(global_image, hd_images_reshape)
310
+ ]
311
+ hd_masks_reshape = [
312
+ torch.cat([_global_mask] + [_mask],
313
+ dim=0) for _global_mask, _mask in zip(
314
+ global_attention_mask, attention_masks_reshape)
315
+ ]
316
+ max_crops = max([img.size(0) for img in hd_images_reshape])
317
+ image_transformed = [
318
+ pad_to_max_num_crops(im, max_crops) for im in hd_images_reshape
319
+ ]
320
+ image_transformed = torch.stack(image_transformed, dim=0)
321
+ mask_transformed = [
322
+ pad_mask_to_max_num_crops(mask, max_crops) \
323
+ for mask in hd_masks_reshape
324
+ ]
325
+ mask_transformed = torch.stack(mask_transformed, dim=0)
326
+
327
+ returned_input_image_embeds = image_transformed
328
+ returned_image_sizes = torch.tensor(shapes, dtype=torch.long)
329
+ returned_image_attention_mask = mask_transformed
330
+ returned_num_img_tokens = num_img_tokens
331
+
332
+ data = {
333
+ "pixel_values": returned_input_image_embeds,
334
+ "image_sizes": returned_image_sizes,
335
+ "image_attention_mask": returned_image_attention_mask,
336
+ "num_img_tokens": returned_num_img_tokens,
337
+ }
338
+ return data
339
+
340
+
341
+class Phi4MMImageEncoder(nn.Module):
342
+ """Image embedding."""
343
+
344
+ def __init__(self,
345
+ config: PretrainedConfig,
346
+ quant_config: Optional[QuantizationConfig],
347
+ prefix: str = "",
348
+ model_dir: str = "") -> None:
349
+ super().__init__()
350
+
351
+ # n_embed or hidden_size
352
+ hidden_size = config.n_embd if hasattr(
353
+ config, 'n_embd') else config.hidden_size
354
+ if hasattr(config, 'embd_pdrop') or hasattr(config, 'embed_pdrop'):
355
+ embd_drop = config.embd_pdrop if hasattr(
356
+ config, 'embd_pdrop') else config.embed_pdrop
357
+ self.drop = nn.Dropout(embd_drop)
358
+ else:
359
+ self.drop = None
360
+
361
+ # layer_idx to output the img features
362
+ if isinstance(config.img_processor, dict):
363
+ self.layer_idx = config.img_processor.get('layer_idx', -2)
364
+ self.type_feature = config.img_processor.get(
365
+ 'type_feature', 'patch')
366
+ else:
367
+ self.layer_idx = -2
368
+ self.type_feature = 'patch'
369
+
370
+ self.img_processor = get_siglip_vision_model(
371
+ _flash_attn_2_enabled=True)
372
+
373
+ pe_weight = self.img_processor.embeddings.position_embedding.weight
374
+ L, D = pe_weight.size()
375
+ H = int(math.sqrt(L))
376
+ assert H**2 == L, f'position embedding size {L} is not square'
377
+ if H % 2 != 0:
378
+ self.img_processor_padding = nn.ReflectionPad2d((0, 1, 0, 1))
379
+ H += 1
380
+ image_dim_out = D
381
+ # ((448/14)//2)**2
382
+ self.num_img_tokens = (H // 2)**2
383
+ self.base_feat_height_target = H
384
+
385
+ self.image_dim_out = image_dim_out
386
+ self.img_sizes = None
387
+ self.image_attention_mask = None
388
+
389
+ # global_gn and sub_gn for hd transform, serves as line separator
390
+ self.use_hd_transform = True
391
+ self.with_learnable_separator = True
392
+ self.hd_transform_order = "sub_glb"
393
+ self.freeze_img_processor = False
394
+ self.crop_size = 448
395
+
396
+ # image token compression
397
+ self.image_token_compression_cls = 'avg_pool_2d'
398
+ self.image_token_compression = nn.AvgPool2d(kernel_size=2, stride=2)
399
+ self.base_feat_height_reduction = 1
400
+ self.base_feat_height_target = self.base_feat_height_target // 2
401
+
402
+ # with_hd_transform and with_learnable_separator should have same value
403
+ assert self.use_hd_transform == self.with_learnable_separator, \
404
+ 'use_hd_transform and with_learnable_separator should have same value'
405
+ assert self.use_hd_transform, \
406
+ 'learnable separator is only for hd transform'
407
+ # 1024 * 4, merge spatial to channel dimension
408
+ self.glb_GN = nn.Parameter(
409
+ torch.zeros([
410
+ 1, 1, self.image_dim_out * self.base_feat_height_reduction**2
411
+ ]))
412
+ self.sub_GN = nn.Parameter(
413
+ torch.zeros([
414
+ 1, 1, 1,
415
+ self.image_dim_out * self.base_feat_height_reduction**2
416
+ ]))
417
+
418
+ dim_projection = hidden_size
419
+ depth = 2
420
+ layers = [
421
+ nn.Linear(image_dim_out * self.base_feat_height_reduction**2,
422
+ dim_projection)
423
+ ]
424
+ for _ in range(1, depth):
425
+ layers.extend(
426
+ [nn.GELU(),
427
+ nn.Linear(dim_projection, dim_projection)])
428
+ self.img_projection = nn.Sequential(*layers)
429
+
430
+ self.vocab_size = config.vocab_size
431
+ self.img_features = None
432
+
433
+ self.use_out_place_operations = False
434
+
435
+ def get_img_features(self,
436
+ img_embeds: torch.FloatTensor,
437
+ attention_mask=None) -> torch.FloatTensor:
438
+ LAYER_IDX = self.layer_idx
439
+ TYPE_FEATURE = self.type_feature
440
+
441
+ img_processor_output = self.img_processor(
442
+ img_embeds,
443
+ output_hidden_states=True,
444
+ patch_attention_mask=attention_mask)
445
+ img_feature = img_processor_output.hidden_states[LAYER_IDX]
446
+
447
+ if TYPE_FEATURE == "patch":
448
+ patch_feature = img_feature
449
+
450
+ use_token_compression = self.image_token_compression is not None
451
+ use_padding = getattr(self, 'img_processor_padding',
452
+ None) is not None
453
+ if use_token_compression or use_padding:
454
+ # reshape to 2D tensor
455
+ width = int(math.sqrt(patch_feature.size(1)))
456
+ patch_feature = patch_feature.view(-1, width, width,
457
+ patch_feature.size(-1))
458
+ # convert to NCHW
459
+ patch_feature = patch_feature.permute(0, 3, 1, 2)
460
+
461
+ if use_padding:
462
+ patch_feature = self.img_processor_padding(patch_feature)
463
+ if use_token_compression:
464
+ patch_feature = self.image_token_compression(patch_feature)
465
+
466
+ # convert to NHWC
467
+ patch_feature = patch_feature.permute(0, 2, 3, 1)
468
+ patch_feature = patch_feature.view(
469
+ -1,
470
+ patch_feature.size(1) * patch_feature.size(2),
471
+ patch_feature.size(-1))
472
+
473
+ return patch_feature
474
+
475
+ raise NotImplementedError
476
+
477
+ def forward(self, pixel_values: torch.FloatTensor,
478
+ image_sizes: torch.Tensor,
479
+ image_attention_mask: torch.Tensor) -> torch.FloatTensor:
480
+ """
481
+ process image and return vision embeddings.
482
+
483
+ pixel_values: (num_images, num_crops, c, h, w)
484
+ image_sizes: [[h1, w1], [h2, w2]]
485
+ image_attention_mask: num_images x num_crops x 32 x 32
486
+ output: (num_images, num_img_tokens, hidden_size)
487
+ """
488
+
489
+ # eg
490
+ # pixel_values: torch.Size([1, 7, 3, 448, 448])
491
+ # image_sizes: tensor([[ 896, 1344]], device='cuda:0')
492
+ # output: torch.Size([1, 1841, 3072])
493
+
494
+ if isinstance(self.img_projection, nn.Sequential):
495
+ target_device = self.img_projection[0].bias.device
496
+ target_dtype = self.img_projection[0].bias.dtype
497
+ else: # It's a single nn.Linear layer
498
+ target_device = self.img_projection.bias.device
499
+ target_dtype = self.img_projection.bias.dtype
500
+
501
+ img_sizes = image_sizes
502
+ num_images, num_crops, c, h, w = pixel_values.shape
503
+ bs = num_images
504
+ pixel_values = pixel_values.flatten(0, 1)
505
+
506
+ img_features = self.get_img_features(
507
+ pixel_values,
508
+ image_attention_mask.type(torch.BoolTensor).flatten(
509
+ 0, 1).to(target_device))
510
+
511
+ base_feat_height_target = self.base_feat_height_target
512
+ base_resolution = self.crop_size
513
+ base_feat_height_reduction = self.base_feat_height_reduction
514
+
515
+ base_feat_height = base_feat_width = int(np.sqrt(
516
+ img_features.shape[1]))
517
+ assert base_feat_height == base_feat_height_target \
518
+ and base_feat_width == base_feat_height_target, \
519
+ f'base_feat_height: {base_feat_height},"\
520
+ f" base_feat_width: {base_feat_width}, "\
521
+ f"expect {base_feat_height_target} features for hd transform'
522
+
523
+ # bs x max_num_crops x (24x24) x C
524
+ img_features = img_features.view(bs, -1,
525
+ base_feat_height * base_feat_width,
526
+ self.image_dim_out)
527
+ C = self.image_dim_out
528
+ H = base_feat_height
529
+
530
+ output_imgs = []
531
+ output_len = []
532
+ # training is tensor, inference is list
533
+ if isinstance(img_sizes, torch.Tensor):
534
+ img_sizes = img_sizes.view(-1, 2)
535
+ for _bs in range(bs):
536
+ h, w = img_sizes[_bs]
537
+ h = h // base_resolution
538
+ w = w // base_resolution
539
+ B_ = h * w
540
+
541
+ # 1 x (24x24) x 1024
542
+ global_img_feature = img_features[_bs, :1]
543
+
544
+ # 1 x 12 x 12 x 4096
545
+ glb_img = global_img_feature.reshape(1, H, H, C).reshape(
546
+ 1, H // base_feat_height_reduction, base_feat_height_reduction,
547
+ H // base_feat_height_reduction, base_feat_height_reduction,
548
+ C).contiguous().permute(0, 1, 3, 2, 4, 5).reshape(
549
+ 1, H // base_feat_height_reduction,
550
+ H // base_feat_height_reduction,
551
+ base_feat_height_reduction * base_feat_height_reduction *
552
+ C).contiguous()
553
+ temp_glb_GN = self.sub_GN.repeat(1,
554
+ H // base_feat_height_reduction,
555
+ 1, 1)
556
+
557
+ # 1 x 156 x 4096
558
+ glb_img = torch.cat([glb_img, temp_glb_GN], dim=2).reshape(
559
+ 1, -1,
560
+ base_feat_height_reduction * base_feat_height_reduction * C)
561
+
562
+ # (max_num_crops-1) x (12x12) x C
563
+ sub_img = img_features[_bs, 1:]
564
+ # 16x574x1024
565
+ # get rid of padding sub_img
566
+ sub_img = sub_img[:B_]
567
+
568
+ # (num_crops, 12, 2, 12, 2, 1024) ->
569
+ # (num_crops, 12, 12, 2, 2, 1024) -> (num_crops, 12*12, 4*1024)
570
+ sub_img = sub_img.reshape(B_, H, H, C).reshape(
571
+ B_, H // base_feat_height_reduction,
572
+ base_feat_height_reduction, H // base_feat_height_reduction,
573
+ base_feat_height_reduction,
574
+ C).contiguous().permute(0, 1, 3, 2, 4, 5).reshape(
575
+ B_, -1, base_feat_height_reduction *
576
+ base_feat_height_reduction * C).contiguous()
577
+ sub_img = sub_img.reshape(
578
+ 1, h, w, base_feat_height // base_feat_height_reduction,
579
+ base_feat_width // base_feat_height_reduction,
580
+ -1).permute(0, 1, 3, 2, 4, 5).reshape(
581
+ 1, h * base_feat_height // base_feat_height_reduction,
582
+ w * base_feat_width // base_feat_height_reduction,
583
+ base_feat_height_reduction * base_feat_height_reduction *
584
+ C)
585
+
586
+ if image_attention_mask is not None and len(
587
+ image_attention_mask) > 0:
588
+ reshaped_image_attention_mask = image_attention_mask[
589
+ _bs, 1:B_ + 1, 0::2, 0::2].reshape(
590
+ 1, h, w,
591
+ base_feat_height // base_feat_height_reduction,
592
+ base_feat_width // base_feat_height_reduction).permute(
593
+ 0, 1, 3, 2, 4).reshape(
594
+ 1, h * base_feat_height //
595
+ base_feat_height_reduction, w *
596
+ base_feat_width // base_feat_height_reduction)
597
+ useful_height = int(
598
+ reshaped_image_attention_mask[0, :, 0].sum().item())
599
+ useful_width = int(
600
+ reshaped_image_attention_mask[0, 0, :].sum().item())
601
+ sub_img = sub_img[:, :useful_height, :useful_width]
602
+ temp_sub_GN = self.sub_GN.repeat(1, useful_height, 1, 1)
603
+ temp_len = int(
604
+ image_attention_mask[_bs, :B_ + 1, 0::2, 0::2].sum().item(
605
+ )) + (useful_height +
606
+ 1) + base_feat_height // base_feat_height_reduction
607
+ else:
608
+ temp_sub_GN = self.sub_GN.repeat(
609
+ 1, h * base_feat_height // base_feat_height_reduction, 1,
610
+ 1)
611
+ temp_len = int((h * w + 1) * self.num_img_tokens + 1 +
612
+ (h + 1) * base_feat_height //
613
+ base_feat_height_reduction)
614
+
615
+ sub_img = torch.cat([sub_img, temp_sub_GN], dim=2).reshape(
616
+ 1, -1,
617
+ base_feat_height_reduction * base_feat_height_reduction * C)
618
+ # (1, num_img_tokens, 1024*4)
619
+
620
+ # glb + sub
621
+ if self.hd_transform_order == 'glb_sub':
622
+ output_imgs.append(
623
+ torch.cat([glb_img, self.glb_GN, sub_img], dim=1))
624
+ elif self.hd_transform_order == 'sub_glb':
625
+ output_imgs.append(
626
+ torch.cat([sub_img, self.glb_GN, glb_img], dim=1))
627
+ else:
628
+ raise NotImplementedError(
629
+ f'hd_transform_order = {self.hd_transform_order}, "\
630
+ "not implemented')
631
+
632
+ #temp_len = int((h*w+1)*144 + 1 + (h+1)*12)
633
+ assert temp_len == output_imgs[-1].shape[
634
+ 1], f'temp_len: {temp_len}, output_imgs[-1].shape[1]: "\
635
+ "{output_imgs[-1].shape[1]}'
636
+
637
+ output_len.append(temp_len)
638
+
639
+ img_set_tensor = []
640
+ for _output_img in output_imgs:
641
+ img_feature_proj = self.img_projection(
642
+ _output_img.to(target_device).to(target_dtype))
643
+ img_set_tensor.append(img_feature_proj)
644
+
645
+ return img_set_tensor
646
+
647
+
648
+class Phi4MMAudioFeatureInputs(TypedDict):
649
+ type: Literal["audio_features"]
650
+ data: Tuple[NestedTensors]
651
+ """Shape: `((batch_size, num_audios, 80, M), )"""
652
+
653
+
654
+class Phi4MMAudioEmbeddingInputs(TypedDict):
655
+ type: Literal["audio_embeds"]
656
+ data: NestedTensors
657
+ """Shape: `(batch_size, num_audios, audio_feature_size, hidden_size)"""
658
+
659
+
660
+Phi4MMAudioInputs = Union[Phi4MMAudioFeatureInputs, Phi4MMAudioEmbeddingInputs]
661
+
662
+
663
+def speechlib_mel(sample_rate, n_fft, n_mels, fmin=None, fmax=None):
664
+ """Create a Mel filter-bank the same as SpeechLib FbankFC.
665
+
666
+ Args:
667
+ sample_rate (int): Sample rate in Hz. number > 0 [scalar]
668
+ n_fft (int): FFT size. int > 0 [scalar]
669
+ n_mel (int): Mel filter size. int > 0 [scalar]
670
+ fmin (float): lowest frequency (in Hz). If None use 0.0.
671
+ float >= 0 [scalar]
672
+ fmax: highest frequency (in Hz). If None use sample_rate / 2.
673
+ float >= 0 [scalar]
674
+
675
+ Returns
676
+ out (numpy.ndarray): Mel transform matrix
677
+ [shape=(n_mels, 1 + n_fft/2)]
678
+ """
679
+
680
+ bank_width = int(n_fft // 2 + 1)
681
+ if fmax is None:
682
+ fmax = sample_rate / 2
683
+ if fmin is None:
684
+ fmin = 0
685
+ assert fmin >= 0, "fmin cannot be negative"
686
+ assert (fmin < fmax <=
687
+ sample_rate / 2), "fmax must be between (fmin, samplerate / 2]"
688
+
689
+ def mel(f):
690
+ return 1127.0 * np.log(1.0 + f / 700.0)
691
+
692
+ def bin2mel(fft_bin):
693
+ return 1127.0 * np.log(1.0 + fft_bin * sample_rate / (n_fft * 700.0))
694
+
695
+ def f2bin(f):
696
+ return int((f * n_fft / sample_rate) + 0.5)
697
+
698
+ # Spec 1: FFT bin range [f2bin(fmin) + 1, f2bin(fmax) - 1]
699
+ klo = f2bin(fmin) + 1
700
+ khi = f2bin(fmax)
701
+
702
+ khi = max(khi, klo)
703
+
704
+ # Spec 2: SpeechLib uses triangles in Mel space
705
+ mlo = mel(fmin)
706
+ mhi = mel(fmax)
707
+ m_centers = np.linspace(mlo, mhi, n_mels + 2)
708
+ ms = (mhi - mlo) / (n_mels + 1)
709
+
710
+ matrix = np.zeros((n_mels, bank_width), dtype=np.float32)
711
+ for m in range(0, n_mels):
712
+ left = m_centers[m]
713
+ center = m_centers[m + 1]
714
+ right = m_centers[m + 2]
715
+ for fft_bin in range(klo, khi):
716
+ mbin = bin2mel(fft_bin)
717
+ if left < mbin < right:
718
+ matrix[m, fft_bin] = 1.0 - abs(center - mbin) / ms
719
+
720
+ return matrix
721
+
722
+
723
+class LogFbankProcessor:
724
+
725
+ def __init__(self):
726
+
727
+ self._eightk_method = "fillzero"
728
+ self._mel = speechlib_mel(16000, 512, 80, fmin=None, fmax=7690).T
729
+
730
+ self._hamming400 = np.hamming(400) # for 16k audio
731
+ self._hamming200 = np.hamming(200) # for 8k audio
732
+
733
+ def extract_spectrogram(self, wav, fs):
734
+ """Extract spectrogram features from waveform.
735
+ Args:
736
+ wav (1D array): waveform of the input
737
+ fs (int): sampling rate of the waveform, 16000 or 8000.
738
+ If fs=8000, the waveform will be resampled to 16000Hz.
739
+ Output:
740
+ log_fbank (2D array): a TxD matrix of log Mel filterbank features.
741
+ D=80, and T is the number of frames.
742
+ """
743
+ if wav.ndim > 1:
744
+ wav = np.squeeze(wav)
745
+
746
+ # by default, we extract the mean if stereo
747
+ if len(wav.shape) == 2:
748
+ wav = wav.mean(1)
749
+
750
+ # Resample to 16000 or 8000 if needed
751
+ if fs > 16000:
752
+ wav = scipy.signal.resample_poly(wav, 1, fs // 16000)
753
+ fs = 16000
754
+ elif 8000 < fs < 16000:
755
+ wav = scipy.signal.resample_poly(wav, 1, fs // 8000)
756
+ fs = 8000
757
+ elif fs < 8000:
758
+ raise RuntimeError(f"Unsupported sample rate {fs}")
759
+
760
+ if fs == 8000:
761
+ if self._eightk_method == "resample":
762
+ # Input audio is 8 kHz. Convert to 16 kHz before feature
763
+ # extraction
764
+ wav = scipy.signal.resample_poly(wav, 2, 1)
765
+ fs = 16000
766
+ # Do nothing here for fillzero method
767
+ elif fs != 16000:
768
+ # Input audio is not a supported sample rate.
769
+ raise RuntimeError(
770
+ f"Input data using an unsupported sample rate: {fs}")
771
+
772
+ preemphasis = 0.97
773
+
774
+ if fs == 8000:
775
+ n_fft = 256
776
+ win_length = 200
777
+ hop_length = 80
778
+ fft_window = self._hamming200
779
+ elif fs == 16000:
780
+ n_fft = 512
781
+ win_length = 400
782
+ hop_length = 160
783
+ fft_window = self._hamming400
784
+
785
+ # Spec 1: SpeechLib cut remaining sample insufficient for a hop
786
+ n_batch = (wav.shape[0] - win_length) // hop_length + 1
787
+ # Here we don't use stride_tricks since the input array may not satisfy
788
+ # memory layout requirement and we need writeable output
789
+ # Here we only use list of views before copy to destination
790
+ # so it is more efficient than broadcasting
791
+ y_frames = np.array(
792
+ [
793
+ wav[_stride:_stride + win_length]
794
+ for _stride in range(0, hop_length * n_batch, hop_length)
795
+ ],
796
+ dtype=np.float32,
797
+ )
798
+
799
+ # Spec 2: SpeechLib applies preemphasis within each batch
800
+ y_frames_prev = np.roll(y_frames, 1, axis=1)
801
+ y_frames_prev[:, 0] = y_frames_prev[:, 1]
802
+ y_frames = (y_frames - preemphasis * y_frames_prev) * 32768
803
+
804
+ S = np.fft.rfft(fft_window * y_frames, n=n_fft,
805
+ axis=1).astype(np.complex64)
806
+
807
+ if fs == 8000:
808
+ # Need to pad the output to look like 16 kHz data but with zeros in
809
+ # the 4 to 8 kHz bins.
810
+ frames, bins = S.shape
811
+ padarray = np.zeros((frames, bins))
812
+ S = np.concatenate((S[:, 0:-1], padarray),
813
+ axis=1) # Nyquist bin gets set to zero
814
+
815
+ spec = np.abs(S).astype(np.float32)
816
+ return spec
817
+
818
+ def extract_features(self, wav, fs):
819
+ """Extract log filterbank features from waveform.
820
+ Args:
821
+ wav (1D array): waveform of the input
822
+ fs (int): sampling rate of the waveform, 16000 or 8000.
823
+ If fs=8000, the waveform will be resampled to 16000Hz.
824
+ Output:
825
+ log_fbank (2D array): a TxD matrix of log Mel filterbank features.
826
+ D=80, and T is the number of frames.
827
+ """
828
+ spec = self.extract_spectrogram(wav, fs)
829
+ spec_power = spec**2
830
+
831
+ fbank_power = np.clip(spec_power.dot(self._mel), 1.0, None)
832
+ log_fbank = np.log(fbank_power).astype(np.float32)
833
+
834
+ return log_fbank
835
+
836
+
837
+@lru_cache
838
+def audio_feature_extractor() -> LogFbankProcessor:
839
+ # Creates an instance of the audio processor, needed to extract the
840
+ # the audio features from the sound file
841
+ # LRU cache ensures that we only make one copy
842
+ return LogFbankProcessor()
843
+
844
+
845
+def _compute_num_image_tokens(image, dynamic_hd_size, vit_image_size,
846
+ vit_patch_size, token_compression_factor):
847
+ """
848
+ compute the number of tokens an image is expected to take up considering
849
+ the image encoder architecture and exclude output features containing
850
+ only padding pixels
851
+
852
+ for siglip, vit_image_size=448, vit_patch_size=14, so output will be
853
+ 32x32 feature map
854
+ NOTE right now, Phi4MM uses hard-coded token_compression_factor=2
855
+ """
856
+ assert vit_image_size % vit_patch_size == 0, \
857
+ "vit_image_size must be divisible by vit_patch_size"
858
+ assert vit_image_size // vit_patch_size % token_compression_factor == 0, \
859
+ "vit_image_size // vit_patch_size must be divisible by "\
860
+ "token_compression_factor"
861
+
862
+ target_aspect_ratio, target_height, target_width = (
863
+ _find_target_aspect_ratio(image,
864
+ vit_image_size,
865
+ dynamic_hd_size,
866
+ min_num=1))
867
+ assert target_aspect_ratio[
868
+ 0] * vit_image_size == target_width, \
869
+ f"{target_aspect_ratio[0]} * {vit_image_size} != {target_width}"
870
+ assert target_aspect_ratio[
871
+ 1] * vit_image_size == target_height, \
872
+ f"{target_aspect_ratio[1]} * {vit_image_size} != {target_height}"
873
+ assert (target_height % vit_image_size == 0
874
+ and target_width % vit_image_size == 0)
875
+
876
+ padding_height, padding_width = _get_padding_size(image, target_height,
877
+ target_width)
878
+ assert padding_width == 0 or padding_height == 0, \
879
+ "padding_width or padding_height must be 0"
880
+
881
+ target_feat_width = target_width // vit_patch_size
882
+ target_feat_height = target_height // vit_patch_size
883
+ if padding_width >= vit_patch_size:
884
+ assert padding_height == 0, "padding_height not 0"
885
+ non_pad_feat_width = target_feat_width - math.floor(
886
+ padding_width / vit_patch_size)
887
+ non_pad_feat_height = target_feat_height
888
+ elif padding_height >= vit_patch_size:
889
+ assert padding_width == 0, "padding_width not 0"
890
+ non_pad_feat_height = target_feat_height - math.floor(
891
+ padding_height / vit_patch_size)
892
+ non_pad_feat_width = target_feat_width
893
+ else:
894
+ # small padding shorter than a vit patch
895
+ non_pad_feat_width = target_feat_width
896
+ non_pad_feat_height = target_feat_height
897
+
898
+ feat_width = non_pad_feat_width // token_compression_factor
899
+ feat_height = non_pad_feat_height // token_compression_factor
900
+ # NOTE it's possible that the non-padding feature is not divisible
901
+ if non_pad_feat_width % token_compression_factor != 0:
902
+ feat_width += 1
903
+ if non_pad_feat_height % token_compression_factor != 0:
904
+ feat_height += 1
905
+ num_hd_patch_tokens = feat_width * feat_height
906
+ num_hd_newline_tokens = feat_height
907
+ vit_feature_size = vit_image_size // vit_patch_size
908
+ num_global_image_tokens = (vit_feature_size // token_compression_factor)**2
909
+ num_sep_tokens = 1
910
+ num_global_image_newline_tokens = \
911
+ vit_feature_size // token_compression_factor
912
+
913
+ return (num_global_image_tokens + num_sep_tokens + num_hd_patch_tokens +
914
+ num_hd_newline_tokens + num_global_image_newline_tokens)
915
+
916
+
917
+def compute_logfbank_output_size(wav_length: int, fs: int) -> Tuple[int, int]:
918
+ """
919
+ Compute the output size of the `extract_features` method.
920
+
921
+ Args:
922
+ wav_length (int): Length of the input waveform in samples.
923
+ fs (int): Sampling rate of the waveform, either 16000 or 8000.
924
+
925
+ Returns:
926
+ tuple (int, int): Output size as (T, D), where:
927
+ T: Number of time frames.
928
+ D: Number of Mel filterbank bins (80).
929
+ """
930
+
931
+ # Resample to 16000 or 8000 if needed
932
+ if fs > 16000:
933
+ wav_length //= fs // 16000
934
+ fs = 16000
935
+ elif 8000 <= fs < 16000:
936
+ # We'll resample to 16K from 8K
937
+ wav_length *= 2
938
+ fs = 16000
939
+ elif fs < 8000:
940
+ raise RuntimeError(f"Unsupported sample rate {fs}")
941
+
942
+ # Spectrogram parameters for 16 kHz
943
+ win_length = 400 # Frame length in samples
944
+ hop_length = 160 # Frame shift in samples
945
+ mel_bins = 80 # Number of mel filterbank bins
946
+
947
+ # Calculate number of frames (T)
948
+ T = (wav_length - win_length) // hop_length + 1
949
+ if T < 1:
950
+ raise ValueError("Waveform too short for given parameters.")
951
+
952
+ # Return time frames (T) and mel bins (D)
953
+ return T, mel_bins
954
+
955
+
956
+def _get_audio_embed_sizes(audios, ctx: InputContext):
957
+ """
958
+ Get the audio embedding sizes for each audio file.
959
+
960
+ Args:
961
+ audios (List[Tuple[np.ndarray, int]]): List of audio files as tuples of
962
+ waveform and sample rate.
963
+ ctx (InputContext): Input context.
964
+
965
+ Returns:
966
+ List[int]: List of audio embedding sizes.
967
+ """
968
+ audio_embed_sizes = []
969
+ for audio in audios:
970
+ audio_data, sf = audio
971
+ audio_frames, _ = compute_logfbank_output_size(len(audio_data), sf)
972
+ audio_embed_size = _compute_audio_embed_size(ctx.get_hf_config(),
973
+ audio_frames)
974
+ audio_embed_sizes.append(audio_embed_size)
975
+ return audio_embed_sizes
976
+
977
+
978
+def _get_audio_id_to_input_ids(audios, ctx: InputContext, prompt_str=""):
979
+ """
980
+ The following will search for `<|audio_{idx}|>` tokens and
981
+ return a mapping of audio placeholder tokens to audio placeholder token ids
982
+ based on the size of the audio embeddings.
983
+
984
+ Args:
985
+ audios (List[Tuple[np.ndarray, int]]): List of audio files as tuples of
986
+ waveform and sample rate.
987
+ ctx (InputContext): Input context.
988
+ prompt_str (str): The prompt string.
989
+
990
+ Returns:
991
+ Dict[str, List[int]]: Mapping of audio placeholder tokens to audio
992
+ placeholder token ids.
993
+
994
+ """
995
+ if len(audios) == 0:
996
+ return {}
997
+
998
+ audio_embed_sizes = _get_audio_embed_sizes(audios, ctx)
999
+ audio_ids = re.findall(AUDIO_TOKEN_PATTERN, prompt_str)
1000
+ audio_ids = [int(audio_id) for audio_id in audio_ids]
1001
+ assert len(audio_ids) == len(
1002
+ audio_embed_sizes
1003
+ ), "Number of audio tokens and audio features do not match"
1004
+ assert tuple(audio_ids) == tuple(range(1,
1005
+ len(audio_ids) +
1006
+ 1)), "Audio ids are not in order!"
1007
+ audio_id_to_input_ids = {
1008
+ f"<|audio_{audio_id}|>":
1009
+ [_AUDIO_PLACEHOLDER_TOKEN_ID] * audio_embed_size
1010
+ for audio_id, audio_embed_size in zip(audio_ids, audio_embed_sizes)
1011
+ }
1012
+
1013
+ return audio_id_to_input_ids
1014
+
1015
+
1016
+def _count_image_tokens(images, ctx: InputContext):
1017
+ hf_config = ctx.get_hf_config()
1018
+ vision_encoder_name = hf_config.img_processor
1019
+ if vision_encoder_name is None:
1020
+ vision_encoder_name = SIGLIP_NAME
1021
+ prepro_config = VISION_ENCODER_TO_PROCESSING_CONFIG[vision_encoder_name]
1022
+ dynamic_hd_size = prepro_config['dynamic_hd']
1023
+ vit_image_size = prepro_config['vit_image_size']
1024
+ vit_patch_size = prepro_config['vit_patch_size']
1025
+ token_compression_factor = prepro_config['token_compression_factor']
1026
+
1027
+ image_token_counts = [
1028
+ _compute_num_image_tokens(image, dynamic_hd_size, vit_image_size,
1029
+ vit_patch_size, token_compression_factor)
1030
+ for image in images
1031
+ ]
1032
+ return image_token_counts
1033
+
1034
+
1035
+def _get_image_id_to_input_ids(images, prompt, ctx: InputContext):
1036
+ if len(images) == 0:
1037
+ return {}
1038
+
1039
+ image_ids = re.findall(IMAGE_TOKEN_PATTERN, prompt)
1040
+ image_ids = [int(image_id) for image_id in image_ids]
1041
+ assert len(image_ids) == len(
1042
+ set(image_ids)), "Duplicate image tokens in prompt"
1043
+ assert len(images) == len(
1044
+ image_ids), "Number of images and image tokens in prompt do not match"
1045
+
1046
+ # NOTE the following assertion is not strictly necessary
1047
+ assert tuple(image_ids) == tuple(range(1,
1048
+ len(image_ids) +
1049
+ 1)), "Image ids are not in order"
1050
+
1051
+ image_token_counts = _count_image_tokens(images, ctx)
1052
+ image_id_to_input_ids = {
1053
+ f"<|image_{image_id}|>": [_IMAGE_PLACEHOLDER_TOKEN_ID] * num_tokens
1054
+ for image_id, num_tokens in zip(image_ids, image_token_counts)
1055
+ }
1056
+ return image_id_to_input_ids
1057
+
1058
+
1059
+def input_processor_for_phi4mm(ctx: InputContext,
1060
+ inputs: DecoderOnlyInputs) -> TokenInputs:
1061
+ """
1062
+ Implements the input processor, which transforms the input prompt ids
1063
+ to include the audio placeholder token. This will become the `input_ids`
1064
+ in `forward` for the model.
1065
+
1066
+ Args:
1067
+ ctx (InputContext): Input context.
1068
+ inputs (DecoderOnlyInputs): The inputs (e.g. prompt, prompt_token_ids)
1069
+ to process.
1070
+
1071
+ Returns:
1072
+ TokenInputs: Processed inputs
1073
+ """
1074
+ multi_modal_data = inputs.get("multi_modal_data")
1075
+ if (multi_modal_data is None or
1076
+ ("audio" not in multi_modal_data and "image" not in multi_modal_data)):
1077
+ # pure text input, so no need to do pre-processing
1078
+ return inputs
1079
+
1080
+ prompt_str = inputs.get("prompt")
1081
+ prompt_token_ids = inputs.get("prompt_token_ids")
1082
+ # for offline_inference, we will get str input and we parse MM special
1083
+ # tokens from it
1084
+ # (ignore prompt_token_ids)
1085
+ # for OAI server, we will get prompt_token_ids, where MM special tokens
1086
+ # are already parsed
1087
+
1088
+ if 'audio' in multi_modal_data:
1089
+ audios = multi_modal_data["audio"]
1090
+
1091
+ if not isinstance(audios, list):
1092
+ audios = [audios]
1093
+ if prompt_str is not None:
1094
+ audio_id_to_input_ids = _get_audio_id_to_input_ids(
1095
+ audios, ctx, prompt_str=prompt_str)
1096
+ audio_embed_sizes = []
1097
+ elif prompt_token_ids is not None:
1098
+ audio_id_to_input_ids = {}
1099
+ audio_embed_sizes = _get_audio_embed_sizes(audios, ctx)
1100
+ else:
1101
+ audio_id_to_input_ids = {}
1102
+ audio_embed_sizes = []
1103
+
1104
+ if 'image' in multi_modal_data:
1105
+ # PIL Image or list of PIL Images
1106
+ images = multi_modal_data["image"]
1107
+ if not isinstance(images, list):
1108
+ images = [images]
1109
+ if prompt_str is not None:
1110
+ image_id_to_input_ids = _get_image_id_to_input_ids(
1111
+ images, prompt_str, ctx)
1112
+ image_token_counts = []
1113
+ elif prompt_token_ids is not None:
1114
+ image_id_to_input_ids = {}
1115
+ image_token_counts = _count_image_tokens(images, ctx)
1116
+ else:
1117
+ image_id_to_input_ids = {}
1118
+ image_token_counts = []
1119
+
1120
+ # Handle the case where the prompt is a string and we need to manually
1121
+ # tokenize it.
1122
+ # In this case, the `audio_id_to_input_ids` dict will be mapping from
1123
+ # an audio placeholder
1124
+ # string (e.g. `<|audio_1|>`) to the audio placeholder tokens for the
1125
+ # given audio length.
1126
+ if prompt_str:
1127
+ pattern = r"(<\|image_\d+\|>|<\|audio_\d+\|>)"
1128
+ prompt_chunk_strings = re.split(pattern, prompt_str)
1129
+ prompt_chunk_strings = [s for s in prompt_chunk_strings if s != ""]
1130
+
1131
+ # Create the new input_ids with the placeholder image and audio
1132
+ # tokens inserted
1133
+ tokenizer = cached_tokenizer_from_config(ctx.model_config)
1134
+ input_ids = []
1135
+ has_imag, has_audio, has_user_text_input = False, False, False
1136
+ for prompt_chunk_string in prompt_chunk_strings:
1137
+ if re.match(IMAGE_TOKEN_PATTERN, prompt_chunk_string):
1138
+ input_ids.extend(image_id_to_input_ids[prompt_chunk_string])
1139
+ has_imag = True
1140
+ elif re.match(AUDIO_TOKEN_PATTERN, prompt_chunk_string):
1141
+ input_ids.extend(audio_id_to_input_ids[prompt_chunk_string])
1142
+ has_audio = True
1143
+ else:
1144
+ curr_token_ids = tokenizer(prompt_chunk_string).input_ids
1145
+ if not has_user_text_input:
1146
+ for token_id in curr_token_ids:
1147
+ if token_id not in NON_USER_INPUT_TOKENS:
1148
+ has_user_text_input = True
1149
+ break
1150
+ input_ids.extend(curr_token_ids)
1151
+ if has_audio and has_imag and has_user_text_input:
1152
+ raise ValueError(
1153
+ "Phi4MMForCausalLM does not support text + audio + image" +
1154
+ " inputs in the same prompt")
1155
+ # Handle the case where the prompt is already tokenized
1156
+ else:
1157
+ assert prompt_token_ids is not None, \
1158
+ "If string prompt isn't provided, prompt_token_ids must be"
1159
+
1160
+ i = 0
1161
+ input_ids = prompt_token_ids
1162
+ # only needed for later assertion
1163
+ img_cnt, audio_cnt, user_text_input_cnt = 0, 0, 0
1164
+ image_token_count_iter = iter(image_token_counts)
1165
+ audio_embed_size_iter = iter(audio_embed_sizes)
1166
+ while i < len(input_ids):
1167
+ token_id = input_ids[i]
1168
+ if token_id == _AUDIO_PLACEHOLDER_TOKEN_ID:
1169
+ token_count = next(audio_embed_size_iter)
1170
+ audio_cnt += 1
1171
+ elif token_id == _IMAGE_PLACEHOLDER_TOKEN_ID:
1172
+ token_count = next(image_token_count_iter)
1173
+ img_cnt += 1
1174
+ else:
1175
+ user_text_input_cnt += 1 if token_id not in \
1176
+ NON_USER_INPUT_TOKENS else 0
1177
+ i += 1
1178
+ continue
1179
+ tokens = [token_id] * token_count
1180
+ input_ids = input_ids[:i] + tokens + input_ids[i + 1:]
1181
+ i += token_count
1182
+
1183
+ if audio_cnt > 0 and img_cnt > 0 and user_text_input_cnt > 0:
1184
+ raise ValueError(
1185
+ "Phi4MMForCausalLM does not support text + audio + image" +
1186
+ " inputs in the same prompt")
1187
+ # If the below assertion fails, it might be that input pure-text
1188
+ # messages contain image/audio special tokens literally
1189
+ # (<|endoftext10|>, <|endoftext11|>).
1190
+ assert (img_cnt == len(image_token_counts)), (
1191
+ f"Number of image tokens in prompt_token_ids ({img_cnt}) "
1192
+ f"does not match number of images ({len(image_token_counts)})")
1193
+ assert (audio_cnt == len(audio_embed_sizes)), (
1194
+ f"Number of audio tokens in prompt_token_ids ({audio_cnt}) "
1195
+ f"does not match number of audios ({len(audio_embed_sizes)})")
1196
+
1197
+ # NOTE: Create a defensive copy of the original inputs
1198
+ return token_inputs(
1199
+ prompt_token_ids=input_ids,
1200
+ prompt=prompt_str,
1201
+ multi_modal_data=multi_modal_data,
1202
+ )
1203
+
1204
+
1205
+def _compute_audio_embed_size(hf_config, audio_frames):
1206
+ """
1207
+ Compute the audio embedding size based on the audio frames and
1208
+ compression rate.
1209
+ """
1210
+ compression_rate = hf_config.embd_layer['audio_embd_layer'][
1211
+ 'compression_rate']
1212
+ # NOTE: this is a hard-coded value but might be configurable in the future
1213
+ qformer_compression_rate = 1
1214
+ integer = audio_frames // compression_rate
1215
+ remainder = audio_frames % compression_rate
1216
+
1217
+ result = integer if remainder == 0 else integer + 1
1218
+
1219
+ integer = result // qformer_compression_rate
1220
+ remainder = result % qformer_compression_rate
1221
+ result = integer if remainder == 0 else integer + 1 # qformer compression
1222
+
1223
+ return result
1224
+
1225
+
1226
+def get_max_phi4mm_audio_tokens(ctx: InputContext) -> int:
1227
+ return 10000
1228
+
1229
+
1230
+def dummy_audio_for_phi4mm(audio_count: int) -> dict:
1231
+ """
1232
+ Create dummy audio data for the Phi4MM model, which is used for profiling.
1233
+
1234
+ Args:
1235
+ audio_count (int): Number of audio samples.
1236
+
1237
+ Returns:
1238
+ dict: Dummy audio data.
1239
+ """
1240
+ dummy_audio = np.full((_AUDIO_MAX_SOUNDFILE_SIZE, ), 0.0)
1241
+ return [(dummy_audio, DUMMY_SAMPLING_FREQUENCY)] * audio_count
1242
+
1243
+
1244
+def dummy_image_for_phi4mm(width: int, height: int):
1245
+ image = Image.new('RGB', (width, height), color='black')
1246
+ return image
1247
+
1248
+
1249
+def dummy_data_for_phi4mm(ctx: InputContext, seq_len: int,
1250
+ mm_counts: Mapping[str, int]) -> DummyData:
1251
+ """
1252
+ Create dummy sequence (input_ids) and audio data for the Phi4MM model,
1253
+ which is used for profiling.
1254
+
1255
+ In this case, the sequence data is a bunch of 0s with a number of audio
1256
+ tokens that correspond to the audio embed size of the
1257
+ _AUDIO_MAX_SOUNDFILE_SIZE.
1258
+
1259
+ Args:
1260
+ ctx (InputContext): Input context.
1261
+ seq_len (int): Length of the sequence.
1262
+ mm_counts (Mapping[str, int]): Multi-modal counts.
1263
+
1264
+ Returns:
1265
+ Tuple: Dummy sequence data and dummy audio data.
1266
+ """
1267
+ audio_count = mm_counts["audio"]
1268
+ audio_frames, _ = compute_logfbank_output_size(_AUDIO_MAX_SOUNDFILE_SIZE,
1269
+ DUMMY_SAMPLING_FREQUENCY)
1270
+ audio_feature_size = _compute_audio_embed_size(ctx.get_hf_config(),
1271
+ audio_frames)
1272
+
1273
+ image_count = mm_counts["image"]
1274
+ dummy_image = get_max_dummy_image(ctx)
1275
+ max_image_tokens = get_max_phi4mm_image_tokens(ctx)
1276
+ total_image_tokens = image_count * max_image_tokens
1277
+
1278
+ if seq_len - audio_feature_size * audio_count - total_image_tokens < 0:
1279
+ raise RuntimeError(
1280
+ f"Phi4MM cannot process {audio_count} audios and {image_count}"
1281
+ f"images in a prompt, please increase max_model_len to be at"
1282
+ f" larger than "
1283
+ f"{audio_feature_size * audio_count + total_image_tokens}"
1284
+ " or reduce audio/image limit by --limit-mm-per-prompt.")
1285
+
1286
+ if audio_feature_size * audio_count > total_image_tokens:
1287
+ seq_data = SequenceData.from_prompt_token_counts(
1288
+ (_AUDIO_PLACEHOLDER_TOKEN_ID, audio_feature_size * audio_count),
1289
+ (0, seq_len - audio_feature_size * audio_count),
1290
+ )
1291
+ mm_data = {
1292
+ "audio": dummy_audio_for_phi4mm(audio_count),
1293
+ }
1294
+ else:
1295
+ seq_data = SequenceData.from_prompt_token_counts(
1296
+ (_IMAGE_PLACEHOLDER_TOKEN_ID, total_image_tokens),
1297
+ (0, seq_len - total_image_tokens),
1298
+ )
1299
+ mm_data = {
1300
+ "image": [dummy_image] * image_count,
1301
+ }
1302
+ return DummyData(seq_data, mm_data)
1303
+
1304
+
1305
+def input_mapper_for_phi4mm_audio(ctx: InputContext,
1306
+ data: object) -> MultiModalInputs:
1307
+ """
1308
+ This function is used to create the MultiModalInputs for the Phi4MM
1309
+ (audio) model.
1310
+ Specifically, for audio, we extract the audio features from the sound
1311
+ file and create pairs of audio features and audio embed lengths (the
1312
+ latter of which is used to repeat the audio placeholder token in the
1313
+ input prompt IDs).
1314
+ These pairs are used, downstream, in `_audio_features_to_embeddings`
1315
+ (via `_process_audio_input`).
1316
+
1317
+ Note that the incoming audio data (each entry in `data`) is a tuple of
1318
+ the audio data and the sampling frequency (e.g. from soundfile.read).
1319
+
1320
+ Args:
1321
+ ctx (InputContext): Input context.
1322
+ data (object): Audio data.
1323
+
1324
+ Returns:
1325
+ MultiModalInputs: Multi-modal inputs.
1326
+ """
1327
+ if not isinstance(data, list):
1328
+ data = [data]
1329
+
1330
+ if len(data) == 0:
1331
+ return MultiModalInputs()
1332
+
1333
+ audio_features = []
1334
+ for audio_input in data:
1335
+ if not isinstance(audio_input, tuple):
1336
+ raise NotImplementedError(
1337
+ f"Unsupported data type: {type(audio_input)}")
1338
+
1339
+ audio, sf = audio_input
1340
+ feature_extractor = audio_feature_extractor()
1341
+ single_audio_features = feature_extractor.extract_features(audio, sf)
1342
+ feat_stride = (1 if not hasattr(feature_extractor, "stride") else
1343
+ feature_extractor.stride)
1344
+ audio_frames = len(single_audio_features) * feat_stride
1345
+ single_audio_embed_size = _compute_audio_embed_size(
1346
+ ctx.get_hf_config(), audio_frames)
1347
+ single_audio_feature_audio_len_pair = (
1348
+ single_audio_features,
1349
+ [single_audio_embed_size],
1350
+ )
1351
+ audio_features.append(single_audio_feature_audio_len_pair)
1352
+ return MultiModalInputs({"audio_features": audio_features})
1353
+
1354
+
1355
+def input_mapper_for_phi4mm_image(ctx: InputContext, data: object):
1356
+ if not isinstance(data, list):
1357
+ data = [data]
1358
+ # data: list of PIL images
1359
+ if len(data) == 0:
1360
+ return MultiModalInputs()
1361
+ hf_config = ctx.get_hf_config()
1362
+ vision_encoder_name = hf_config.img_processor
1363
+ if vision_encoder_name is None:
1364
+ vision_encoder_name = SIGLIP_NAME
1365
+ prepro_config = VISION_ENCODER_TO_PROCESSING_CONFIG[vision_encoder_name]
1366
+ dynamic_hd_size = prepro_config['dynamic_hd']
1367
+ vit_image_size = prepro_config['vit_image_size']
1368
+ vit_patch_size = prepro_config['vit_patch_size']
1369
+
1370
+ image_input_dict = preprocess(data, dynamic_hd_size, vit_image_size,
1371
+ vit_patch_size)
1372
+ return MultiModalInputs({
1373
+ "pixel_values":
1374
+ image_input_dict["pixel_values"],
1375
+ "image_sizes":
1376
+ image_input_dict["image_sizes"],
1377
+ "image_attention_mask":
1378
+ image_input_dict["image_attention_mask"],
1379
+ "num_img_tokens":
1380
+ image_input_dict["num_img_tokens"],
1381
+ })
1382
+
1383
+
1384
+def cat_with_pad(tensors, dim, padding_value=0):
1385
+ """
1386
+ cat along dim, while pad to max for all other dims
1387
+ """
1388
+ ndim = tensors[0].dim()
1389
+ assert all(
1390
+ t.dim() == ndim for t in
1391
+ tensors[1:]), "All tensors must have the same number of dimensions"
1392
+
1393
+ out_size = [max(t.shape[i] for t in tensors) for i in range(ndim)]
1394
+ out_size[dim] = sum(t.shape[dim] for t in tensors)
1395
+ output = tensors[0].new_full(out_size, padding_value)
1396
+
1397
+ index = 0
1398
+ for t in tensors:
1399
+ # Create a slice list where every dimension except dim is full slice
1400
+ slices = [slice(0, t.shape[d]) for d in range(ndim)]
1401
+ # Update only the concat dimension slice
1402
+ slices[dim] = slice(index, index + t.shape[dim])
1403
+
1404
+ output[slices] = t
1405
+ index += t.shape[dim]
1406
+
1407
+ return output
1408
+
1409
+
1410
+@MULTIMODAL_REGISTRY.register_input_mapper("audio",
1411
+ input_mapper_for_phi4mm_audio)
1412
+@MULTIMODAL_REGISTRY.register_input_mapper("image",
1413
+ input_mapper_for_phi4mm_image)
1414
+@MULTIMODAL_REGISTRY.register_max_multimodal_tokens(
1415
+ "audio", get_max_phi4mm_audio_tokens)
1416
+@MULTIMODAL_REGISTRY.register_max_multimodal_tokens(
1417
+ "image", get_max_phi4mm_image_tokens)
1418
+@INPUT_REGISTRY.register_dummy_data(dummy_data_for_phi4mm)
1419
+@INPUT_REGISTRY.register_input_processor(input_processor_for_phi4mm)
1420
+class Phi4MMForCausalLM(nn.Module, SupportsLoRA, SupportsMultiModal):
1421
+ """
1422
+ Implements the Phi-4-multimodal-instruct model in VLLM.
1423
+ """
1424
+ # LoRA specific attributes
1425
+ packed_modules_mapping = {
1426
+ "qkv_proj": [
1427
+ "qkv_proj",
1428
+ ],
1429
+ "gate_up_proj": [
1430
+ "gate_up_proj",
1431
+ ],
1432
+ }
1433
+ supported_lora_modules = [
1434
+ "qkv_proj", "o_proj", "gate_up_proj", "down_proj"
1435
+ ]
1436
+ # Phi4MMForCausalLM does not apply LoRA to the embedding layer.
1437
+ embedding_modules = {}
1438
+ embedding_padding_modules = []
1439
+
1440
+ def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
1441
+ super().__init__()
1442
+ config = vllm_config.model_config.hf_config
1443
+ multimodal_config = vllm_config.model_config.multimodal_config
1444
+ assert multimodal_config, "multimodal_config is required"
1445
+ quant_config = vllm_config.quant_config
1446
+ lora_config = vllm_config.lora_config
1447
+
1448
+ self.config = config
1449
+ self.multimodal_config = multimodal_config
1450
+ self.quant_config = quant_config
1451
+ self.lora_config = lora_config
1452
+
1453
+ # Tensor/Pipeline parallel not supported for now.
1454
+ assert get_tensor_model_parallel_world_size(
1455
+ ) == 1, "tensor parallel is not supported"
1456
+ assert get_pp_group(
1457
+ ).world_size == 1, "pipeline parallel is not supported"
1458
+
1459
+ self.vision_encoder = Phi4MMImageEncoder(
1460
+ config,
1461
+ quant_config,
1462
+ prefix="model.vision_embed_tokens",
1463
+ model_dir=config._name_or_path)
1464
+
1465
+ if isinstance(config.embd_layer["audio_embd_layer"], dict):
1466
+ embedding_config = {
1467
+ "embedding_cls":
1468
+ config.embd_layer["audio_embd_layer"]["embedding_cls"],
1469
+ **config.embd_layer["audio_embd_layer"],
1470
+ }
1471
+ else:
1472
+ embedding_config = {
1473
+ "embedding_cls": self.config.embd_layer["embedding_cls"]
1474
+ }
1475
+
1476
+ self.embed_tokens_extend = AudioEmbedding(config, **embedding_config)
1477
+ self.model = LlamaModel(vllm_config=vllm_config,
1478
+ prefix=maybe_prefix(prefix, "model"))
1479
+
1480
+ self.unpadded_vocab_size = config.vocab_size
1481
+ if lora_config:
1482
+ self.unpadded_vocab_size += lora_config.lora_extra_vocab_size
1483
+ self.lm_head = ParallelLMHead(
1484
+ self.unpadded_vocab_size,
1485
+ config.hidden_size,
1486
+ org_num_embeddings=config.vocab_size,
1487
+ padding_size=(
1488
+ DEFAULT_VOCAB_PADDING_SIZE
1489
+ # We need bigger padding if using lora for kernel
1490
+ # compatibility
1491
+ if not lora_config else lora_config.lora_vocab_padding_size),
1492
+ quant_config=quant_config,
1493
+ )
1494
+ if config.tie_word_embeddings:
1495
+ self.lm_head = self.lm_head.tie_weights(self.model.embed_tokens)
1496
+ logit_scale = getattr(config, "logit_scale", 1.0)
1497
+ self.logits_processor = LogitsProcessor(self.unpadded_vocab_size,
1498
+ config.vocab_size, logit_scale)
1499
+ self.sampler = Sampler()
1500
+
1501
+ def _audio_features_to_embeddings(
1502
+ self,
1503
+ input_ids: torch.Tensor,
1504
+ input_features: List[torch.Tensor],
1505
+ audio_input_sizes: torch.Tensor,
1506
+ audio_projection_mode: str,
1507
+ ) -> torch.Tensor:
1508
+ """
1509
+ Convert audio features to embeddings, which are used as input to the
1510
+ model (via `inputs_embeds`).
1511
+
1512
+ Args:
1513
+ input_ids (torch.Tensor): Input IDs (the prompt in this case).
1514
+ input_features (list[torch.Tensor]): Input features (the audio
1515
+ embeddings).
1516
+ audio_input_sizes (list[torch.Tensor]): Audio input sizes (the
1517
+ audio embed lengths to use for padding the audio placeholder token
1518
+ in the input prompt IDs).
1519
+ """
1520
+ # The audio projection can either be a single linear or Sequential,
1521
+ # so handle both cases
1522
+ if isinstance(self.embed_tokens_extend.audio_projection,
1523
+ nn.Sequential):
1524
+ target_dtype = self.embed_tokens_extend.audio_projection[
1525
+ 0].bias.dtype
1526
+ else:
1527
+ target_dtype = self.embed_tokens_extend.audio_projection.bias.dtype
1528
+
1529
+ audio_input = [
1530
+ input.unsqueeze(0).to(target_dtype) for input in input_features
1531
+ ]
1532
+ kwargs = {
1533
+ "wte": self.model.embed_tokens,
1534
+ 'audio_projection_mode': audio_projection_mode
1535
+ }
1536
+ audio_embeddings = self.embed_tokens_extend(input_ids, audio_input,
1537
+ audio_input_sizes,
1538
+ **kwargs)
1539
+ audio_embeddings = audio_embeddings.to(target_dtype)
1540
+ return audio_embeddings
1541
+
1542
+ def _parse_and_validate_audio_input(
1543
+ self, **kwargs: object) -> Optional[Phi4MMAudioInputs]:
1544
+ """
1545
+ Parse and validate the audio input to the model. This handles both
1546
+ audio features and audio embeddings, but only the former is used for
1547
+ now.
1548
+
1549
+ Args:
1550
+ kwargs (object): Keyword arguments.
1551
+
1552
+ Returns:
1553
+ Optional[Phi4MMAudioInputs]: Parsed and validated audio inputs.
1554
+ """
1555
+ audio_features = kwargs.pop("audio_features", None)
1556
+ audio_embeds = kwargs.pop("audio_embeds", None)
1557
+
1558
+ if audio_features is None and audio_embeds is None:
1559
+ return None
1560
+
1561
+ if audio_features is not None:
1562
+ if not isinstance(audio_features, (torch.Tensor, list)):
1563
+ raise ValueError("Incorrect type of audio features. "
1564
+ f"Got type: {type(audio_features)}")
1565
+
1566
+ return Phi4MMAudioFeatureInputs(type="audio_features",
1567
+ data=audio_features)
1568
+
1569
+ if audio_embeds is not None:
1570
+ if not isinstance(audio_embeds, (torch.Tensor, list)):
1571
+ raise ValueError("Incorrect type of audio embeds. "
1572
+ f"Got type: {type(audio_embeds)}")
1573
+
1574
+ return Phi4MMAudioEmbeddingInputs(type="audio_embeds",
1575
+ data=audio_embeds)
1576
+
1577
+ raise AssertionError("This line should be unreachable.")
1578
+
1579
+ def _process_audio_input(self, input_ids: torch.Tensor,
1580
+ audio_input: Phi4MMAudioInputs,
1581
+ audio_projection_mode: str) -> NestedTensors:
1582
+ """
1583
+ Create the audio embeddings from the audio input, where the audio input
1584
+ is pairs of audio features and audio embed lengths. The audio input is
1585
+ created by `input_mapper_for_phi4mm_audio`.
1586
+
1587
+ Args:
1588
+ input_ids (torch.Tensor): Input IDs (the prompt in this case,
1589
+ before the audio token replication).
1590
+ audio_input (Phi4MMAudioInputs): Audio input.
1591
+
1592
+ Returns:
1593
+ NestedTensors: Audio embeddings
1594
+ """
1595
+ if audio_input["type"] == "audio_embeds":
1596
+ return audio_input["data"]
1597
+
1598
+ audio_features = audio_input["data"]
1599
+ # (e.g. multiple examples) and the second dim is the multi-audio dim
1600
+ # (e.g. multiple audios in the same example)
1601
+ audio_feature = [i[0] for j in audio_features for i in j]
1602
+ audio_feature_len = [i[1].item() for j in audio_features for i in j]
1603
+ # Add the batch dim via `squeeze`
1604
+
1605
+ return self._audio_features_to_embeddings(
1606
+ input_ids.unsqueeze(0),
1607
+ audio_feature,
1608
+ audio_feature_len,
1609
+ audio_projection_mode,
1610
+ ).squeeze(0)
1611
+
1612
+ def _parse_and_validate_image_input(self,
1613
+ **kwargs: object) -> Optional[Dict]:
1614
+ pixel_values: Optional[Dict] = kwargs.get("pixel_values")
1615
+ if pixel_values is None:
1616
+ return None
1617
+
1618
+ image_sizes = kwargs.get("image_sizes")
1619
+ image_attention_mask = kwargs.get("image_attention_mask")
1620
+ num_img_tokens = kwargs.get("num_img_tokens")
1621
+ assert image_sizes is not None and image_attention_mask is not None\
1622
+ and num_img_tokens is not None, "Missing image inputs"
1623
+
1624
+ if isinstance(pixel_values, list):
1625
+ assert pixel_values[0].dim() == 5, "Incorrect image inputs"
1626
+ # list len is batch_size.
1627
+ # each tensor has dimension: num_img_per_example, num_hd_patches,
1628
+ # channels, height, width.
1629
+ # need to pad along num_hd_patches.
1630
+ # mask size num_img_per_prompt, num_hd_patches, feat_h, heat_w.
1631
+ pixel_values = cat_with_pad(pixel_values, dim=0)
1632
+ elif isinstance(pixel_values, torch.Tensor):
1633
+ # dimension: batch_size, num_img_per_example, num_hd_patches,
1634
+ # channels, height, width.
1635
+ # we flatten first 2 dims to make it a single large batch for
1636
+ # SigLIP Encoder.
1637
+ assert pixel_values.dim() == 6, "Incorrect image inputs"
1638
+ pixel_values = pixel_values.flatten(0, 1)
1639
+ else:
1640
+ raise ValueError("Incorrect pixel_values inputs")
1641
+
1642
+ if isinstance(image_attention_mask, list):
1643
+ image_attention_mask = cat_with_pad(image_attention_mask, dim=0)
1644
+ elif isinstance(image_attention_mask, torch.Tensor):
1645
+ image_attention_mask = image_attention_mask.flatten(0, 1)
1646
+ else:
1647
+ raise ValueError("Incorrect image_attention_mask inputs")
1648
+
1649
+ if isinstance(image_sizes, list):
1650
+ image_sizes = torch.cat(image_sizes, dim=0)
1651
+ elif isinstance(image_sizes, torch.Tensor):
1652
+ image_sizes = image_sizes.flatten(0, 1)
1653
+ else:
1654
+ raise ValueError("Incorrect image_attention_mask inputs")
1655
+
1656
+ if isinstance(num_img_tokens, list):
1657
+ num_img_tokens = [
1658
+ n for num_tensor in num_img_tokens
1659
+ for n in num_tensor.tolist()
1660
+ ]
1661
+ elif isinstance(num_img_tokens, torch.Tensor):
1662
+ num_img_tokens = num_img_tokens.flatten(0, 1).tolist()
1663
+ else:
1664
+ raise ValueError("Incorrect image_attention_mask inputs")
1665
+
1666
+ return {
1667
+ 'pixel_values': pixel_values,
1668
+ 'image_sizes': image_sizes,
1669
+ 'image_attention_mask': image_attention_mask,
1670
+ 'num_img_tokens': num_img_tokens,
1671
+ }
1672
+
1673
+ def merge_image_features_to_inputs_embeds(
1674
+ self,
1675
+ input_ids: torch.Tensor,
1676
+ inputs_embeds: torch.Tensor,
1677
+ image_set_tensors: List[torch.Tensor],
1678
+ ):
1679
+ position_tuple = (input_ids == _IMAGE_PLACEHOLDER_TOKEN_ID).nonzero(
1680
+ as_tuple=True)
1681
+
1682
+ assert all([t.shape[0] == 1 for t in image_set_tensors
1683
+ ]), 'img_set_tensor should have shape (1, N_tokens, C)'
1684
+ # Shape: (merged_N_tokens, C)
1685
+ image_set_tensor = torch.cat(image_set_tensors, dim=1).squeeze(0)
1686
+ image_set_tensor = image_set_tensor.to(inputs_embeds.dtype).to(
1687
+ inputs_embeds.device)
1688
+ merged_embeds = inputs_embeds.index_put(
1689
+ indices=position_tuple,
1690
+ values=image_set_tensor,
1691
+ accumulate=False,
1692
+ )
1693
+ return merged_embeds
1694
+
1695
+ def load_weights(self, weights: Iterable[Tuple[str,
1696
+ torch.Tensor]]) -> None:
1697
+ weights = {name: weight for name, weight in weights}
1698
+ adjusted_weights = {}
1699
+
1700
+ for name, weight in weights.items():
1701
+ # NOTE vision-speech tasks use a separate projection layer
1702
+ audio_proj_4v = \
1703
+ "model.embed_tokens_extend.audio_embed.audio_projection.vision"
1704
+ if name.startswith(audio_proj_4v):
1705
+ name = name.replace(
1706
+ audio_proj_4v,
1707
+ "embed_tokens_extend.audio_projection_for_vision")
1708
+
1709
+ name = (name.replace(
1710
+ "model.embed_tokens_extend.audio_embed."\
1711
+ "audio_projection.speech.",
1712
+ "embed_tokens_extend.audio_projection.",
1713
+ ).replace(
1714
+ "model.embed_tokens_extend.audio_embed.",
1715
+ "embed_tokens_extend.",
1716
+ ).replace("model.embed_tokens_extend.image_embed.",
1717
+ "vision_encoder."))
1718
+ # NOTE: this is deal with LoRA injection, where `base_layer`
1719
+ # remains as the original layer in the model
1720
+ if name.endswith(".base_layer.weight"):
1721
+ name = name.replace(".base_layer.weight", ".weight")
1722
+ adjusted_weights[name] = weight
1723
+
1724
+ missing_keys, unexpected_keys = self.load_state_dict(adjusted_weights,
1725
+ strict=False)
1726
+ logger.debug("*** missing keys:")
1727
+ for key in missing_keys:
1728
+ logger.debug(key)
1729
+ logger.debug("**** unexpected keys:")
1730
+ for key in unexpected_keys:
1731
+ logger.debug(key)
1732
+
1733
+ def forward(
1734
+ self,
1735
+ input_ids: torch.Tensor,
1736
+ positions: torch.Tensor,
1737
+ intermediate_tensors: Optional[IntermediateTensors] = None,
1738
+ **kwargs: object,
1739
+ ) -> torch.Tensor:
1740
+ if intermediate_tensors is not None:
1741
+ input_ids = None
1742
+ inputs_embeds = None
1743
+ else:
1744
+ # Each entry in this is a pair of audio_features and audio_embed
1745
+ # lengths
1746
+ audio_input = self._parse_and_validate_audio_input(**kwargs)
1747
+ image_inputs = self._parse_and_validate_image_input(**kwargs)
1748
+
1749
+ has_audio = audio_input is not None
1750
+ has_image = image_inputs is not None
1751
+
1752
+ if has_audio:
1753
+ audio_projection_mode = 'vision' if has_image else 'speech'
1754
+ inputs_embeds = self._process_audio_input(
1755
+ input_ids, audio_input, audio_projection_mode)
1756
+
1757
+ if has_image:
1758
+ dtype = self.vision_encoder.img_processor.embeddings.\
1759
+ patch_embedding.weight.dtype
1760
+ pixel_values = image_inputs['pixel_values'].to(dtype)
1761
+ image_sizes = image_inputs['image_sizes']
1762
+ image_attention_mask = image_inputs['image_attention_mask']
1763
+ image_set_tensors = self.vision_encoder(
1764
+ pixel_values, image_sizes, image_attention_mask)
1765
+ if not has_audio:
1766
+ inputs_embeds = self.model.embed_tokens(input_ids)
1767
+
1768
+ inputs_embeds = self.merge_image_features_to_inputs_embeds(
1769
+ input_ids, inputs_embeds, image_set_tensors)
1770
+
1771
+ if has_image or has_audio:
1772
+ # multi-modal input, we have set inputs_embeds properly in
1773
+ # previous steps
1774
+ input_ids = None
1775
+ else:
1776
+ # text-only, we keep using original input_ids
1777
+ inputs_embeds = None
1778
+
1779
+ hidden_states = self.model(
1780
+ input_ids,
1781
+ positions,
1782
+ intermediate_tensors,
1783
+ inputs_embeds=inputs_embeds,
1784
+ )
1785
+
1786
+ return hidden_states
1787
+
1788
+ def compute_logits(
1789
+ self,
1790
+ hidden_states: torch.Tensor,
1791
+ sampling_metadata: SamplingMetadata,
1792
+ ) -> Optional[torch.Tensor]:
1793
+ logits = self.logits_processor(self.lm_head, hidden_states,
1794
+ sampling_metadata)
1795
+ return logits
1796
+
1797
+ def sample(
1798
+ self,
1799
+ logits: torch.Tensor,
1800
+ sampling_metadata: SamplingMetadata,
1801
+ ) -> Optional[SamplerOutput]:
1802
+ next_tokens = self.sampler(logits, sampling_metadata)
1803
+ return next_tokens
vllm/model_executor/models/phi4mm_audio.py
new
+1403
@@ -0,0 +1,1403 @@
1
+# SPDX-License-Identifier: Apache-2.0
2
+# Copyright (c) Microsoft Corporation.
3
+# Licensed under the MIT license.
4
+# Code copied from Microsoft/MoE by Jacob Platin (jacobplatin@microsoft.com)
5
+# but implemented by the Phi-Speech team
6
+#!/usr/bin/env python3
7
+import abc
8
+import math
9
+from functools import partial
10
+from typing import Callable, Dict, List, Literal, Optional, Union
11
+
12
+import numpy as np
13
+import torch
14
+import torch.nn.functional as F
15
+from torch import Tensor, nn
16
+from torch.distributed.algorithms._checkpoint.checkpoint_wrapper import (
17
+ CheckpointImpl, CheckpointWrapper, checkpoint_wrapper, offload_wrapper)
18
+from torch.distributed.fsdp.fully_sharded_data_parallel import (
19
+ FullyShardedDataParallel)
20
+from torch.utils.checkpoint import checkpoint
21
+from transformers import PretrainedConfig
22
+
23
+from vllm.model_executor.models.phi4mm_utils import (
24
+ AbsolutePositionalEncoding, ConvModule, FeedForward, MeanVarianceNormLayer,
25
+ MultiHeadedAttention, NemoConvSubsampling, T5RelativeAttentionLogitBias,
26
+ adaptive_enc_mask, attn_checkpointing, embedding_checkpoint_wrapper,
27
+ get_offset, repeat, unfold_tensor, validate_checkpointing_config)
28
+
29
+_AUDIO_PLACEHOLDER_TOKEN_ID = 200011 # <|endoftext11|>
30
+
31
+
32
+def encoder_checkpoint_wrapper(
33
+ activation_checkpointing: Union[str, Dict],
34
+ layer_cls: type,
35
+ idx: int = 0,
36
+) -> Callable:
37
+ """return encoder activation checkpoint wrapper"""
38
+ validate_checkpointing_config(activation_checkpointing)
39
+
40
+ if isinstance(activation_checkpointing, str):
41
+ if activation_checkpointing:
42
+ if activation_checkpointing == "offload":
43
+ return offload_wrapper
44
+ return partial(checkpoint_wrapper)
45
+ return lambda x: x
46
+
47
+ if isinstance(activation_checkpointing, dict):
48
+ target_layer_cls = activation_checkpointing.get(
49
+ "module", "transformer")
50
+ if target_layer_cls.lower() == "transformer":
51
+ target_layer_cls = (
52
+ "EncoderLayer",
53
+ "ConformerEncoderLayer",
54
+ )
55
+ elif target_layer_cls.lower() == "attention":
56
+ target_layer_cls = ("MultiHeadedAttention", "MultiHeadAttention")
57
+ checkpointing_interval = activation_checkpointing.get("interval", 1)
58
+ offloading = activation_checkpointing.get("offload", False)
59
+ impl = (CheckpointImpl.REENTRANT if activation_checkpointing.get(
60
+ "reentrant", True) else CheckpointImpl.NO_REENTRANT)
61
+
62
+ if (idx % checkpointing_interval == 0
63
+ and layer_cls.__name__ in target_layer_cls):
64
+ if offloading:
65
+ return offload_wrapper
66
+ return partial(checkpoint_wrapper, checkpoint_impl=impl)
67
+ return lambda x: x
68
+
69
+ raise ValueError("Invalid activation_checkpointing config")
70
+
71
+
72
+class ConformerEncoderLayer(nn.Module):
73
+ """ConformerEncoder Layer module.
74
+ for more details see conformer paper:
75
+ https://arxiv.org/abs/2005.08100
76
+ This module implement the Conformer block layer.
77
+
78
+ Args:
79
+ d_model: int
80
+ attention dim.
81
+ ext_pw_out_channel: int
82
+ if > 0, ext_pw_out_channel is a dim channel size
83
+ for the last pointwise conv after swish activation.
84
+ depthwise_seperable_out_channel: int
85
+ if set different to 0, the number of
86
+ depthwise_seperable_out_channel will be used as a
87
+ channel_out of the second conv1d layer.
88
+ otherwise, it equal to 0, the second conv1d layer is skipped.
89
+ depthwise_multiplier: int
90
+ number of input_dim channels duplication. this value
91
+ will be used to compute the hidden channels of the Conv1D.
92
+ n_head: int
93
+ the number of heads for multihead attention module.
94
+ d_ffn: int
95
+ output size of the feed_forward blocks.
96
+ ext_pw_kernel_size: int
97
+ kernel size of the conv pointwise of the conformer.
98
+ kernel_size: int
99
+ kernel size.
100
+ dropout_rate: float
101
+ dropout rate.
102
+ causal: bool, optional
103
+ if set to True, convolution have no access
104
+ to future frames. default False.
105
+ batch_norm: bool, optional
106
+ if set to True, apply batchnorm before activation
107
+ in ConvModule layer of the conformer.
108
+ default False
109
+ activation: str, optional
110
+ activation function name,
111
+ one of ["relu", "swish", "sigmoid"],
112
+ sigmoid activation is only used with "glu_in_fnn=True",
113
+ default "relu".
114
+ chunk_se: int, optional
115
+ 0 for offline SE.
116
+ 1 for streaming SE, where mean is computed
117
+ by accumulated history until current chunk_se.
118
+ 2 for streaming SE, where mean is computed
119
+ by only the current chunk.
120
+ default 0.
121
+ chunk_size: int, optional
122
+ chunk_size for cnn. default 18
123
+ conv_activation: str, optional
124
+ activation function used in ConvModule part
125
+ of the conformer, default "relu".
126
+ conv_glu_type: str, optional
127
+ activation function used for the glu inside
128
+ the ConvModule part of the conformer.
129
+ default: "sigmoid".
130
+ bias_in_glu: bool, optional
131
+ if set to True, use additive bias in the weight module
132
+ before GLU.
133
+ linear_glu_in_convm: bool, optional
134
+ if set to True, use GLULinear module,
135
+ otherwise, used GLUPointWiseConv module.
136
+ default to False.
137
+ attention_innner_dim: int, optional
138
+ if equal to -1, attention dim for linears k/q/v is
139
+ equal to d_model. otherwise attention_innner_dim is used.
140
+ default -1.
141
+ attention_glu_type: str, optional
142
+ activation function for glu used in the multihead attention,
143
+ default "swish".
144
+ activation_checkpointing: str, optional
145
+ a dictionarry of {"module","interval","offload"}, where
146
+ "module": str
147
+ accept ["transformer", "attention"] to select
148
+ which module should do activation checkpointing.
149
+ "interval": int, default 1,
150
+ interval of applying activation checkpointing,
151
+ interval = 1 means that we apply checkpointing
152
+ on every layer (if activation), otherwise,
153
+ we apply it every x interval.
154
+ "offload": bool, default False,
155
+ if set to True, we offload activation to cpu and
156
+ reload it during backward, otherwise,
157
+ we recalculate activation in backward.
158
+ default "".
159
+ export: bool, optional
160
+ if set to True, it remove the padding from convolutional layers
161
+ and allow the onnx conversion for inference.
162
+ default False.
163
+ use_pt_scaled_dot_product_attention: bool, optional
164
+ if set to True, use pytorch's scaled dot product attention
165
+ implementation in training.
166
+ attn_group_sizes: int, optional
167
+ the number of groups to use for attention, default 1
168
+ (Multi-Head Attention),
169
+ 1 = typical Multi-Head Attention,
170
+ 1 < attn_group_sizes < attention_heads = Grouped-Query Attention
171
+ attn_group_sizes = attenion_heads = Multi-Query Attention
172
+ """
173
+
174
+ def __init__(
175
+ self,
176
+ d_model=512,
177
+ ext_pw_out_channel=0,
178
+ depthwise_seperable_out_channel=256,
179
+ depthwise_multiplier=1,
180
+ n_head=4,
181
+ d_ffn=2048,
182
+ ext_pw_kernel_size=1,
183
+ kernel_size=3,
184
+ dropout_rate=0.1,
185
+ causal=False,
186
+ batch_norm=False,
187
+ activation="relu",
188
+ chunk_se=0,
189
+ chunk_size=18,
190
+ conv_activation="relu",
191
+ conv_glu_type="sigmoid",
192
+ bias_in_glu=True,
193
+ linear_glu_in_convm=False,
194
+ attention_innner_dim=-1,
195
+ attention_glu_type="swish",
196
+ activation_checkpointing="",
197
+ export=False,
198
+ use_pt_scaled_dot_product_attention=False,
199
+ attn_group_sizes: int = 1,
200
+ ):
201
+ super().__init__()
202
+
203
+ self.feed_forward_in = FeedForward(
204
+ d_model=d_model,
205
+ d_inner=d_ffn,
206
+ dropout_rate=dropout_rate,
207
+ activation=activation,
208
+ bias_in_glu=bias_in_glu,
209
+ )
210
+
211
+ self.self_attn = encoder_checkpoint_wrapper(
212
+ activation_checkpointing,
213
+ MultiHeadedAttention,
214
+ )(MultiHeadedAttention(
215
+ n_head,
216
+ d_model,
217
+ dropout_rate,
218
+ attention_innner_dim,
219
+ attention_glu_type,
220
+ bias_in_glu,
221
+ use_pt_scaled_dot_product_attention=
222
+ use_pt_scaled_dot_product_attention,
223
+ group_size=attn_group_sizes,
224
+ ))
225
+ self.conv = ConvModule(
226
+ d_model,
227
+ ext_pw_out_channel,
228
+ depthwise_seperable_out_channel,
229
+ ext_pw_kernel_size,
230
+ kernel_size,
231
+ depthwise_multiplier,
232
+ dropout_rate,
233
+ causal,
234
+ batch_norm,
235
+ chunk_se,
236
+ chunk_size,
237
+ conv_activation,
238
+ conv_glu_type,
239
+ bias_in_glu,
240
+ linear_glu_in_convm,
241
+ export=export,
242
+ )
243
+
244
+ self.feed_forward_out = FeedForward(
245
+ d_model=d_model,
246
+ d_inner=d_ffn,
247
+ dropout_rate=dropout_rate,
248
+ activation=activation,
249
+ bias_in_glu=bias_in_glu,
250
+ )
251
+
252
+ self.layer_norm_att = nn.LayerNorm(d_model)
253
+ self.layer_norm = nn.LayerNorm(d_model)
254
+
255
+ def forward(
256
+ self,
257
+ x,
258
+ pos_k,
259
+ pos_v,
260
+ mask,
261
+ relative_attention_bias: Optional[Tensor] = None,
262
+ ):
263
+ """ConformerEncoder forward.
264
+
265
+ Args:
266
+ x: torch.Tensor
267
+ input feature of shape (batch, max_time_in, size)
268
+ pos_k: torch.Tensor
269
+ positional key embedding.
270
+ mask: torch.Tensor
271
+ mask for x (batch, max_time_in)
272
+ relative_attention_bias: Optional[torch.Tensor]
273
+ bias added to attention logits w.r.t. relative positions
274
+ (1, n_head, time1, time2)
275
+ """
276
+ x = x + 0.5 * self.feed_forward_in(x)
277
+ norm_x = self.layer_norm_att(x)
278
+
279
+ x = x + self.self_attn(
280
+ norm_x,
281
+ norm_x,
282
+ norm_x,
283
+ pos_k,
284
+ pos_v,
285
+ mask,
286
+ relative_attention_bias=relative_attention_bias,
287
+ )
288
+ x = x + self.conv(x)
289
+ x = x + 0.5 * self.feed_forward_out(x)
290
+
291
+ out = self.layer_norm(x)
292
+
293
+ return out, pos_k, pos_v, mask
294
+
295
+
296
+class TransformerEncoderBase(abc.ABC, nn.Module):
297
+ """The Base class for Transformer based encoders
298
+
299
+ Please set causal = True in streaming model
300
+ Args:
301
+ input_size: int
302
+ input feature dimension.
303
+ chunk_size: int, list(int)
304
+ Number of frames for each chunk
305
+ This variable can take 2 forms:
306
+ int: Used for inference, or single chunk size training
307
+ list(int) : Used only for variable chunk size training
308
+ Some examples for the 2 cases:
309
+ chunk_size = 12
310
+ chunk_size = [6, 8, 12, 24]
311
+ left_chunk: int, list(int)
312
+ Number of chunks used for masking in streaming mode.
313
+ This variable can take 2 forms:
314
+ int: Used for inference, or single chunk size training
315
+ list(int) : Used only for variable chunk size training. When
316
+ chunk_size is a list, left_chunk must be a list with same length.
317
+ Some examples for the 2 cases:
318
+ left_chunk = 6
319
+ left_chunk = [12, 9, 6, 3]
320
+ attention_dim: int, optional
321
+ attention dimension. default 256.
322
+ attention_heads: int, optional
323
+ the number of heads. default 4
324
+ input_layer: str, optional
325
+ input layer type before Conformer,
326
+ one of ["linear", "conv2d", "custom", "vgg2l", "embed"],
327
+ default "conv2d"
328
+ cnn_out: int, optional
329
+ the number of CNN channels before Conformer.
330
+ default -1.
331
+ cnn_layer_norm: bool, optional
332
+ layer norm between Conformer and the first CNN.
333
+ default False.
334
+ time_reduction: int, optional
335
+ time reduction factor
336
+ default 4
337
+ dropout_rate: float, optional
338
+ dropout rate. default 0.1
339
+ padding_idx: int, optional
340
+ padding index for input_layer=embed
341
+ default -1
342
+ relative_attention_bias_args: dict, optional
343
+ use more efficient scalar bias-based relative multihead attention
344
+ (Q*K^T + B) implemented in cmb.basics.embedding.
345
+ [T5/ALiBi]RelativeAttentionLogitBias
346
+ usage: relative_attention_bias_args={"type": t5/alibi}
347
+ additional method-specific arguments can be provided (see
348
+ transformer_base.py)
349
+ positional_dropout_rate: float, optional
350
+ dropout rate after positional encoding. default 0.0
351
+ nemo_conv_settings: dict, optional
352
+ A dictionary of settings for NeMo Subsampling.
353
+ default None
354
+ conv2d_extra_padding: str, optional
355
+ Add extra padding in conv2d subsampling layers. Choices are
356
+ (feat, feat_time, none, True).
357
+ if True or feat_time, the extra padding is added into non full
358
+ supraframe utts in batch.
359
+ Default: none
360
+ attention_group_size: int, optional
361
+ the number of groups to use for attention, default 1
362
+ (Multi-Head Attention),
363
+ 1 = typical Multi-Head Attention,
364
+ 1 < attention_group_size < attention_heads = Grouped-Query
365
+ Attention
366
+ attention_group_size = attenion_heads = Multi-Query Attention
367
+ """
368
+
369
+ def __init__(
370
+ self,
371
+ input_size,
372
+ chunk_size,
373
+ left_chunk,
374
+ attention_dim=256,
375
+ attention_heads=4,
376
+ input_layer="nemo_conv",
377
+ cnn_out=-1,
378
+ cnn_layer_norm=False,
379
+ time_reduction=4,
380
+ dropout_rate=0.0,
381
+ padding_idx=-1,
382
+ relative_attention_bias_args=None,
383
+ positional_dropout_rate=0.0,
384
+ nemo_conv_settings=None,
385
+ conv2d_extra_padding: Literal["feat", "feat_time", "none",
386
+ True] = "none",
387
+ attention_group_size=1,
388
+ encoder_embedding_config=None,
389
+ ):
390
+ super().__init__()
391
+ self.input_size = input_size
392
+ self.input_layer = input_layer
393
+ self.chunk_size = chunk_size
394
+ self.left_chunk = left_chunk
395
+ self.attention_dim = attention_dim
396
+ self.num_heads = attention_heads
397
+ self.attention_group_size = attention_group_size
398
+ self.time_reduction = time_reduction
399
+ self.nemo_conv_settings = nemo_conv_settings
400
+ self.encoder_embedding_config = encoder_embedding_config
401
+
402
+ if self.input_layer == "nemo_conv":
403
+ default_nemo_conv_settings = {
404
+ "subsampling": "dw_striding",
405
+ "subsampling_factor": self.time_reduction,
406
+ "feat_in": input_size,
407
+ "feat_out": attention_dim,
408
+ "conv_channels": 256,
409
+ "subsampling_conv_chunking_factor": 1,
410
+ "activation": nn.ReLU(),
411
+ "is_causal": False,
412
+ }
413
+ # Override any of the defaults with the incoming, user settings
414
+ if nemo_conv_settings:
415
+ default_nemo_conv_settings.update(nemo_conv_settings)
416
+ for i in ["subsampling_factor", "feat_in", "feat_out"]:
417
+ assert (
418
+ i not in nemo_conv_settings
419
+ ), "{i} should be specified outside of the NeMo dictionary"
420
+
421
+ self.embed = NemoConvSubsampling(**default_nemo_conv_settings, )
422
+ else:
423
+ raise ValueError("unknown input_layer: " + input_layer)
424
+
425
+ self.pos_emb = AbsolutePositionalEncoding(attention_dim,
426
+ positional_dropout_rate)
427
+
428
+ self.relative_attention_bias_type = (
429
+ relative_attention_bias_args.get("type")
430
+ if relative_attention_bias_args else None)
431
+ if self.relative_attention_bias_type == "t5":
432
+ assert (self.num_heads % self.attention_group_size == 0
433
+ ), "attention_group_size must divide n_head"
434
+ self.relative_attention_bias_layer = T5RelativeAttentionLogitBias(
435
+ self.num_heads // self.attention_group_size,
436
+ max_distance=relative_attention_bias_args.get(
437
+ "t5_bias_max_distance", 1000),
438
+ symmetric=relative_attention_bias_args.get(
439
+ "t5_bias_symmetric", False),
440
+ )
441
+ else:
442
+ raise NotImplementedError
443
+
444
+ def post_init(self, init_model_config):
445
+
446
+ pretrained_speech_encoder_path = init_model_config.get(
447
+ "pretrained_speech_encoder_path", None)
448
+ if pretrained_speech_encoder_path:
449
+ model_state = torch.load(pretrained_speech_encoder_path,
450
+ map_location="cpu")
451
+ encoder_state_dict = {}
452
+ for k, v in model_state.items():
453
+ if "encoder." in k:
454
+ tmp_k = k.replace("encoder.", "")
455
+ encoder_state_dict[tmp_k] = v
456
+
457
+ if hasattr(self, "encoder_embedding"):
458
+ del self.encoder_embedding
459
+ self.load_state_dict(encoder_state_dict)
460
+
461
+ if not hasattr(self, "encoder_embedding"):
462
+ self.encoder_embedding = MeanVarianceNormLayer(
463
+ self.encoder_embedding_config["input_size"])
464
+
465
+ def compute_lens_change(self, feature_lens):
466
+ """feature_lens: int
467
+ return updated feature lens.
468
+
469
+ This used to return a different lambda function for each case that
470
+ computed the right thing. That does not work within Torchscript.
471
+ If you really need this to be faster, create nn.Module()-s for all
472
+ the cases and return one of them. Torchscript does support that.
473
+ """
474
+ if self.input_layer == "nemo_conv":
475
+ # Handle the special causal case
476
+ subsampling_causal_cond = self.nemo_conv_settings.get(
477
+ "subsampling", "dw_striding") in [
478
+ "dw_striding",
479
+ "striding",
480
+ "striding_conv1d",
481
+ ]
482
+ is_causal = self.nemo_conv_settings.get("is_causal", False)
483
+ if is_causal and subsampling_causal_cond:
484
+ lens_change = (torch.ceil(feature_lens /
485
+ self.time_reduction).long()
486
+ if isinstance(feature_lens, Tensor) else
487
+ math.ceil(feature_lens / self.time_reduction))
488
+ feature_lens_remainder = feature_lens % self.time_reduction
489
+ if isinstance(feature_lens, Tensor):
490
+ lens_change[feature_lens_remainder != 1] += 1
491
+ elif feature_lens_remainder != 1:
492
+ lens_change += 1
493
+ return lens_change
494
+ ceil_func = (math.ceil
495
+ if isinstance(feature_lens, int) else torch.ceil)
496
+ return ceil_func(feature_lens / self.time_reduction)
497
+
498
+ @abc.abstractmethod
499
+ def forward(self):
500
+ """Abstract forward method implementation."""
501
+
502
+ def _chunk_size_selection(self, chunk_size=None, left_chunk=None):
503
+ """If chunk size is a list, we will randomly select a chunk size."""
504
+
505
+ if chunk_size is None:
506
+ chunk_size = self.chunk_size
507
+ if left_chunk is None:
508
+ left_chunk = self.left_chunk
509
+ if isinstance(chunk_size, list):
510
+ # Variable chunk size during training
511
+ chunk_size_index = int(
512
+ torch.randint(low=0, high=len(chunk_size), size=(1, )))
513
+ chunk_size_train_eff = chunk_size[chunk_size_index]
514
+ if not isinstance(left_chunk, list):
515
+ raise ValueError(
516
+ "Since chunk_size is a list, left_chunk must be a list")
517
+ if len(left_chunk) != len(chunk_size):
518
+ raise ValueError(
519
+ "The length of left_chunk must be the same as length of "\
520
+ "chunk_size."
521
+ )
522
+ left_chunk_train_eff = left_chunk[chunk_size_index]
523
+ else:
524
+ chunk_size_train_eff = chunk_size
525
+ left_chunk_train_eff = left_chunk
526
+
527
+ return chunk_size_train_eff, left_chunk_train_eff
528
+
529
+ def _get_embed_class(self, embed):
530
+ # pylint: disable=protected-access
531
+ is_embed_using_act_chkpt = isinstance(embed, CheckpointWrapper)
532
+ is_embed_fsdp_wrapped = isinstance(embed, FullyShardedDataParallel)
533
+ embed_class = embed
534
+ if is_embed_using_act_chkpt:
535
+ embed_class = embed._checkpoint_wrapped_module
536
+ if is_embed_fsdp_wrapped:
537
+ embed_class = embed.module
538
+ return embed_class
539
+
540
+ def _forward_embeddings_core(self, input_tensor, masks):
541
+ embed_class = self._get_embed_class(self.embed)
542
+ assert isinstance(embed_class, NemoConvSubsampling)
543
+ input_tensor, masks = self.embed(input_tensor, masks)
544
+ return input_tensor, masks
545
+
546
+ def _position_embedding(self, input_tensor):
547
+ pos_k = None
548
+ pos_v = None
549
+ if self.relative_attention_bias_layer is None:
550
+ input_tensor = self.pos_emb(
551
+ input_tensor) # default to add abs sinusoid embedding
552
+ return pos_k, pos_v
553
+
554
+ def _streaming_mask(self, seq_len, batch_size, chunk_size, left_chunk):
555
+ chunk_size_train_eff, left_chunk_train_eff = \
556
+ self._chunk_size_selection(chunk_size, left_chunk)
557
+
558
+ # Create mask matrix for streaming
559
+ # S stores start index. if chunksize is 18, s is [0,18,36,....]
560
+ chunk_start_idx = np.arange(0, seq_len, chunk_size_train_eff)
561
+ # avoid randomness when run evaluation or decoding
562
+ if self.training and np.random.rand() > 0.5:
563
+ # Either first or last chunk is not complete.
564
+ # If only the last one is not complete, EOS is not effective
565
+ chunk_start_idx = seq_len - chunk_start_idx
566
+ chunk_start_idx = chunk_start_idx[::-1]
567
+ chunk_start_idx = chunk_start_idx[:-1]
568
+ chunk_start_idx = np.insert(chunk_start_idx, 0, 0)
569
+
570
+ enc_streaming_mask = (adaptive_enc_mask(
571
+ seq_len, chunk_start_idx,
572
+ left_window=left_chunk_train_eff).unsqueeze(0).expand(
573
+ [batch_size, -1, -1]))
574
+ return enc_streaming_mask
575
+
576
+ def forward_embeddings(self,
577
+ xs_pad,
578
+ masks,
579
+ chunk_size_nc=None,
580
+ left_chunk_nc=None):
581
+ """Forwarding the inputs through the top embedding layers
582
+
583
+ Args:
584
+ xs_pad: torch.Tensor
585
+ input tensor
586
+ masks: torch.Tensor
587
+ input mask
588
+ chunk_size_nc: (optional, default is None) chunk size for
589
+ non-causal layers
590
+ left_chunk_nc: (optional, default is None) # of left chunks for
591
+ non-causal layers
592
+ """
593
+ # pylint: disable=R0915
594
+ # get new lens.
595
+ seq_len = int(self.compute_lens_change(xs_pad.shape[1]))
596
+ if seq_len <= 0:
597
+ raise ValueError(
598
+ f"""The sequence length after time reduction is invalid:
599
+ {seq_len}. Your input feature is too short. Consider
600
+ filtering out the very short sentence from data
601
+ loader""", )
602
+
603
+ batch_size = xs_pad.shape[0]
604
+
605
+ enc_streaming_mask = self._streaming_mask(seq_len, batch_size,
606
+ self.chunk_size,
607
+ self.left_chunk)
608
+
609
+ if xs_pad.is_cuda:
610
+ enc_streaming_mask = enc_streaming_mask.cuda()
611
+ xs_pad = xs_pad.cuda()
612
+
613
+ input_tensor = xs_pad
614
+ input_tensor, masks = self._forward_embeddings_core(
615
+ input_tensor, masks)
616
+
617
+ streaming_mask = enc_streaming_mask
618
+ if streaming_mask is not None and masks is not None:
619
+ hs_mask = masks & streaming_mask
620
+ elif masks is not None:
621
+ hs_mask = masks
622
+ else:
623
+ hs_mask = streaming_mask
624
+
625
+ if chunk_size_nc is not None:
626
+ enc_streaming_mask_nc = self._streaming_mask(
627
+ seq_len, batch_size, chunk_size_nc, left_chunk_nc)
628
+ if xs_pad.is_cuda:
629
+ enc_streaming_mask_nc = enc_streaming_mask_nc.cuda()
630
+ if masks is not None:
631
+ hs_mask_nc = masks & enc_streaming_mask_nc
632
+ else:
633
+ hs_mask_nc = enc_streaming_mask_nc
634
+ else:
635
+ hs_mask_nc = None
636
+
637
+ pos_k, pos_v = self._position_embedding(input_tensor)
638
+
639
+ if chunk_size_nc is None:
640
+ return input_tensor, pos_k, pos_v, hs_mask, masks
641
+ return input_tensor, pos_k, pos_v, hs_mask, masks, hs_mask_nc
642
+
643
+ def get_offset(self):
644
+ """Returns offset used when retaining inputs for decoding.
645
+
646
+ This is essentially, how many additional frames have to be added to
647
+ the front-end CNN input to ensure it can produce a single output.
648
+ So if the "padding" parameter is 0, typically offset will be > 0.
649
+ """
650
+ return get_offset(self.input_layer, self.time_reduction)
651
+
652
+
653
+class ConformerEncoder(TransformerEncoderBase):
654
+ """ConformerEncoder module.
655
+ see original paper for more details:
656
+ https://arxiv.org/abs/2005.08100
657
+
658
+ Please set causal = True in streaming model
659
+ Args:
660
+ input_size: int
661
+ input feature dimension.
662
+ chunk_size: int, list(int)
663
+ Number of frames for each chunk
664
+ This variable can take 2 forms:
665
+ int: Used for inference, or single chunk size training
666
+ list(int) : Used only for variable chunk size training
667
+ Some examples for the 2 cases:
668
+ chunk_size = 12
669
+ chunk_size = [6, 8, 12, 24]
670
+ left_chunk: int, list(int)
671
+ Number of chunks used for masking in streaming mode.
672
+ This variable can take 2 forms:
673
+ int: Used for inference, or single chunk size training
674
+ list(int) : Used only for variable chunk size training. When
675
+ chunk_size is a list, left_chunk must be a list with same length.
676
+ Some examples for the 2 cases:
677
+ left_chunk = 6
678
+ left_chunk = [12, 9, 6, 3]
679
+ left_chunk: int
680
+ number of chunks used for masking in streaming mode.
681
+ num_lang: int
682
+ This parameter is used to store the number of languages in the
683
+ lang_dict, only used for multiseed/multilingual models.
684
+ default None.
685
+ attention_dim: int, optional
686
+ attention dimension. default 256.
687
+ attention_heads: int, optional
688
+ the number of heads. default 4
689
+ linear_units:
690
+ the number of units of position-wise feed forward.
691
+ default 2048
692
+ num_block:
693
+ number of Transformer layer. default 6
694
+ dropout_rate: float, optional
695
+ dropout rate. default 0.1
696
+ input_layer: str, optional
697
+ input layer type before Conformer,
698
+ one of ["linear", "conv2d", "custom", "vgg2l", "embed"],
699
+ default "conv2d"
700
+ causal: bool, optional
701
+ if set to True, convolution have no access
702
+ to future frames. default False.
703
+ batch_norm: bool, optional
704
+ if set to True, apply batchnorm before activation
705
+ in ConvModule layer of the conformer.
706
+ default False
707
+ cnn_out: int, optional
708
+ the number of CNN channels before Conformer.
709
+ default -1.
710
+ cnn_layer_norm: bool, optional
711
+ layer norm between Conformer and the first CNN.
712
+ default False.
713
+ ext_pw_out_channel: int, optional
714
+ the number of channel for CNN
715
+ before depthwise_seperable_CNN.
716
+ If 0 then use linear. default 0.
717
+ ext_pw_kernel_size: int, optional
718
+ kernel size of N before depthwise_seperable_CNN.
719
+ only work for ext_pw_out_channel > 0.
720
+ default 1
721
+ depthwise_seperable_out_channel: int, optional
722
+ the number of channel for
723
+ depthwise_seperable_CNN.
724
+ default 256.
725
+ depthwise_multiplier: int, optional
726
+ the number of multiplier for
727
+ depthwise_seperable_CNN.
728
+ default 1.
729
+ chunk_se: int, optional
730
+ 0 for offline SE.
731
+ 1 for streaming SE, where mean is computed
732
+ by accumulated history until current chunk_se.
733
+ 2 for streaming SE, where mean is computed
734
+ by only the current chunk.
735
+ default 0.
736
+ kernel_size: int, optional
737
+ the number of kernels for depthwise_seperable_CNN.
738
+ default 3.
739
+ activation: str, optional
740
+ FeedForward block activation.
741
+ one of ["relu", "swish", "sigmoid"]
742
+ default "relu".
743
+ conv_activation: str, optional
744
+ activation function used in ConvModule part
745
+ of the conformer, default "relu".
746
+ conv_glu_type: str, optional
747
+ activation used use glu in depthwise_seperable_CNN,
748
+ default "sigmoid"
749
+ bias_in_glu: bool, optional
750
+ if set to True, use additive bias in the weight module
751
+ before GLU. default True
752
+ linear_glu_in_convm: bool, optional
753
+ if set to True, use GLULinear module,
754
+ otherwise, used GLUPointWiseConv module.
755
+ default to False.
756
+ attention_glu_type: str
757
+ only work for glu_in_attention !=0
758
+ default "swish".
759
+ export: bool, optional
760
+ if set to True, it remove the padding from convolutional layers
761
+ and allow the onnx conversion for inference.
762
+ default False.
763
+ activation_checkpointing: str, optional
764
+ a dictionarry of {"module","interval","offload"}, where
765
+ "module": str
766
+ accept ["transformer", "attention"] to select
767
+ which module should do activation checkpointing.
768
+ "interval": int, default 1,
769
+ interval of applying activation checkpointing,
770
+ interval = 1 means that we apply checkpointing
771
+ on every layer (if activation), otherwise,
772
+ we apply it every x interval.
773
+ "offload": bool, default False,
774
+ if set to True, we offload activation to cpu and
775
+ reload it during backward, otherwise,
776
+ we recalculate activation in backward.
777
+ default "".
778
+ extra_layer_output_idx: int
779
+ the layer index to be exposed.
780
+ relative_attention_bias_args: dict, optional
781
+ use more efficient scalar bias-based relative multihead attention
782
+ (Q*K^T + B) implemented in cmb.basics.embedding.
783
+ [T5/ALiBi]RelativeAttentionLogitBias
784
+ usage: relative_attention_bias_args={"type": t5/alibi}
785
+ additional method-specific arguments can be provided (see
786
+ transformer_base.py)
787
+ time_reduction: int optional
788
+ time reduction factor
789
+ default 4
790
+ use_pt_scaled_dot_product_attention: whether to use pytorch scaled
791
+ dot product attention in training.
792
+ Default: False
793
+ nemo_conv_settings: dict, optional
794
+ A dictionary of settings for NeMo Subsampling.
795
+ default: None
796
+ usage: nemo_conv_settings=
797
+ {
798
+ "subsampling":
799
+ dw_striding/striding/dw_striding_conv1d/striding_conv1d,
800
+ "conv_channels": int,
801
+ "subsampling_conv_chunking_factor": int,
802
+ "is_causal": True/False
803
+ }
804
+ conv2d_extra_padding: str, optional
805
+ Add extra padding in conv2d subsampling layers. Choices are
806
+ (feat, feat_time, none, True)
807
+ Default: none
808
+ replication_pad_for_subsample_embedding: For batched-streaming
809
+ decoding, use "replication" padding for the cache at start of
810
+ utterance.
811
+ Default: False
812
+ attention_group_size: int, optional
813
+ the number of groups to use for attention, default 1
814
+ (Multi-Head Attention),
815
+ 1 = typical Multi-Head Attention,
816
+ 1 < attention_group_size < attention_heads = Grouped-Query
817
+ Attention
818
+ attention_group_size = attenion_heads = Multi-Query Attention
819
+ """
820
+
821
+ extra_multi_layer_output_idxs: List[int]
822
+
823
+ def __init__( # pylint: disable-all
824
+ self,
825
+ input_size,
826
+ chunk_size,
827
+ left_chunk,
828
+ num_lang=None,
829
+ attention_dim=256,
830
+ attention_heads=4,
831
+ linear_units=2048,
832
+ num_blocks=6,
833
+ dropout_rate=0.1,
834
+ input_layer="nemo_conv",
835
+ causal=True,
836
+ batch_norm=False,
837
+ cnn_out=-1,
838
+ cnn_layer_norm=False,
839
+ ext_pw_out_channel=0,
840
+ ext_pw_kernel_size=1,
841
+ depthwise_seperable_out_channel=256,
842
+ depthwise_multiplier=1,
843
+ chunk_se=0,
844
+ kernel_size=3,
845
+ activation="relu",
846
+ conv_activation="relu",
847
+ conv_glu_type="sigmoid",
848
+ bias_in_glu=True,
849
+ linear_glu_in_convm=False,
850
+ attention_glu_type="swish",
851
+ export=False,
852
+ extra_layer_output_idx=-1,
853
+ extra_multi_layer_output_idxs=[], # noqa
854
+ activation_checkpointing="",
855
+ relative_attention_bias_args=None,
856
+ time_reduction=4,
857
+ use_pt_scaled_dot_product_attention=False,
858
+ nemo_conv_settings=None,
859
+ conv2d_extra_padding: Literal["feat", "feat_time", "none",
860
+ True] = "none",
861
+ replication_pad_for_subsample_embedding=False,
862
+ attention_group_size=1,
863
+ encoder_embedding_config=None,
864
+ ):
865
+ super().__init__(
866
+ input_size,
867
+ chunk_size,
868
+ left_chunk,
869
+ attention_dim,
870
+ attention_heads,
871
+ input_layer,
872
+ cnn_out,
873
+ cnn_layer_norm,
874
+ time_reduction,
875
+ dropout_rate=dropout_rate,
876
+ relative_attention_bias_args=relative_attention_bias_args,
877
+ positional_dropout_rate=0.0,
878
+ nemo_conv_settings=nemo_conv_settings,
879
+ conv2d_extra_padding=conv2d_extra_padding,
880
+ attention_group_size=attention_group_size,
881
+ encoder_embedding_config=encoder_embedding_config,
882
+ )
883
+ self.num_blocks = num_blocks
884
+ self.num_lang = num_lang
885
+ self.kernel_size = kernel_size
886
+ self.embed = embedding_checkpoint_wrapper(activation_checkpointing)(
887
+ self.embed)
888
+ self.replication_pad_for_subsample_embedding: bool = (
889
+ replication_pad_for_subsample_embedding)
890
+ assert (self.num_heads % attention_group_size == 0
891
+ ), "attention_group_size must divide n_head"
892
+ self.num_heads_k = self.num_heads // attention_group_size
893
+
894
+ self.encoders = repeat(
895
+ num_blocks,
896
+ lambda i: encoder_checkpoint_wrapper(activation_checkpointing,
897
+ ConformerEncoderLayer, i)
898
+ (ConformerEncoderLayer(
899
+ d_model=attention_dim,
900
+ ext_pw_out_channel=ext_pw_out_channel,
901
+ depthwise_seperable_out_channel=
902
+ depthwise_seperable_out_channel,
903
+ depthwise_multiplier=depthwise_multiplier,
904
+ n_head=attention_heads,
905
+ d_ffn=linear_units,
906
+ ext_pw_kernel_size=ext_pw_kernel_size,
907
+ kernel_size=kernel_size,
908
+ dropout_rate=dropout_rate,
909
+ causal=causal,
910
+ batch_norm=batch_norm,
911
+ activation=activation,
912
+ chunk_se=chunk_se,
913
+ chunk_size=chunk_size,
914
+ conv_activation=conv_activation,
915
+ conv_glu_type=conv_glu_type,
916
+ bias_in_glu=bias_in_glu,
917
+ linear_glu_in_convm=linear_glu_in_convm,
918
+ attention_glu_type=attention_glu_type,
919
+ activation_checkpointing=attn_checkpointing(
920
+ activation_checkpointing, i),
921
+ export=export,
922
+ use_pt_scaled_dot_product_attention=
923
+ use_pt_scaled_dot_product_attention,
924
+ attn_group_sizes=attention_group_size,
925
+ )),
926
+ )
927
+ self.extra_layer_output_idx = extra_layer_output_idx
928
+ self.extra_multi_layer_output_idxs = extra_multi_layer_output_idxs
929
+ # Make a zeros scalar we can use in get_initial_state to determine
930
+ # the device and the needed dtype:
931
+ self.register_buffer("dev_type", torch.zeros(()), persistent=False)
932
+
933
+ def init_relative_attention_bias(self, input_tensor):
934
+ if self.relative_attention_bias_layer:
935
+ return self.relative_attention_bias_layer(input_tensor)
936
+
937
+ def calculate_hs_mask(self, xs_pad, device, mask):
938
+ max_audio_length = xs_pad.shape[1]
939
+ batch_size = xs_pad.shape[0]
940
+ enc_streaming_mask = self._streaming_mask(max_audio_length, batch_size,
941
+ self.chunk_size,
942
+ self.left_chunk)
943
+ enc_streaming_mask = enc_streaming_mask.to(device)
944
+ if mask is None:
945
+ return enc_streaming_mask
946
+
947
+ feature_lens = mask.sum(1)
948
+ padding_length = feature_lens
949
+ pad_mask = (torch.arange(0, max_audio_length,
950
+ device=device).expand(padding_length.size(0),
951
+ -1)
952
+ < padding_length.unsqueeze(1))
953
+ pad_mask = pad_mask.unsqueeze(1)
954
+ pad_mask = pad_mask & enc_streaming_mask
955
+ return pad_mask
956
+
957
+ @torch.jit.ignore
958
+ def forward(self, xs_pad, masks):
959
+ """Conformer Forward function
960
+
961
+ Args:
962
+ xs_pad: torch.Tensor
963
+ input tensor
964
+ masks: torch.Tensor
965
+ post-embedding input lengths
966
+ """
967
+ xs_pad = self.encoder_embedding(xs_pad)
968
+ input_tensor, pos_k, pos_v, hs_mask, masks = self.forward_embeddings(
969
+ xs_pad, masks)
970
+
971
+ unfolded = False
972
+ ori_bz, seq_len, D = input_tensor.shape
973
+ max_seq_len = 500 #maximum position for absolute positional encoding
974
+ if seq_len > max_seq_len:
975
+ # audio sequence is longer than max_seq_len, unfold it into chunks
976
+ # of max_seq_len
977
+ unfolded = True
978
+ # the unfold op will drop residual frames, pad it to the multiple
979
+ # of max_seq_len
980
+ if seq_len % max_seq_len > 0:
981
+ chunk_pad_size = max_seq_len - (seq_len % max_seq_len)
982
+ else:
983
+ chunk_pad_size = 0
984
+ if chunk_pad_size > 0:
985
+ input_tensor_pad = F.pad(input_tensor,
986
+ (0, 0, 0, chunk_pad_size), "constant",
987
+ 0)
988
+ input_tensor = input_tensor_pad.to(input_tensor.device)
989
+ input_tensor = unfold_tensor(input_tensor, max_seq_len)
990
+ if masks is not None:
991
+ # revise hs_mask here because the previous calculated hs_mask
992
+ # did not consider extra pad
993
+ subsampled_pad_mask = masks.squeeze(
994
+ 1) # [bz, subsampled_unmask_seq_len]
995
+ extra_padded_subsamlped_pad_mask = F.pad(
996
+ subsampled_pad_mask, (0, chunk_pad_size), "constant",
997
+ False) # extra padding to the pad mask
998
+ extra_padded_subsamlped_pad_mask = \
999
+ extra_padded_subsamlped_pad_mask.unsqueeze(-1).float()
1000
+ masks_unfold = unfold_tensor(
1001
+ extra_padded_subsamlped_pad_mask, max_seq_len
1002
+ ) # unfold the pad mask like we did to the input tensor
1003
+ masks_unfold = masks_unfold.squeeze(
1004
+ -1).bool() # unfold op does not support bool tensor
1005
+ else:
1006
+ masks_unfold = None
1007
+ hs_mask = self.calculate_hs_mask(
1008
+ input_tensor, input_tensor.device, masks_unfold
1009
+ ) # calculate hs_mask based on the unfolded pad mask
1010
+
1011
+ # layer_emb = None
1012
+
1013
+ relative_attention_bias = self.init_relative_attention_bias(
1014
+ input_tensor)
1015
+
1016
+ _simplified_path = (self.extra_layer_output_idx == -1
1017
+ and relative_attention_bias is None)
1018
+
1019
+ if _simplified_path:
1020
+ input_tensor, *_ = self.encoders(input_tensor, pos_k, pos_v,
1021
+ hs_mask)
1022
+ else:
1023
+ for i, layer in enumerate(self.encoders):
1024
+ input_tensor, _, _, _ = layer(
1025
+ input_tensor,
1026
+ pos_k,
1027
+ pos_v,
1028
+ hs_mask,
1029
+ relative_attention_bias=relative_attention_bias,
1030
+ )
1031
+
1032
+ # if i == self.extra_layer_output_idx:
1033
+ # layer_emb = input_tensor
1034
+
1035
+ if unfolded:
1036
+ embed_dim = input_tensor.shape[-1]
1037
+ input_tensor = input_tensor.reshape(ori_bz, -1, embed_dim)
1038
+ # if we ever padded before unfolding, we need to remove the padding
1039
+ if chunk_pad_size > 0:
1040
+ input_tensor = input_tensor[:, :-chunk_pad_size, :]
1041
+
1042
+ return input_tensor, masks # , layer_emb
1043
+
1044
+ def gradient_checkpointing_enable(self):
1045
+ pass
1046
+
1047
+
1048
+class WindowQformer(nn.Module):
1049
+ """Window-level Qformer"""
1050
+
1051
+ def __init__(
1052
+ self,
1053
+ window_size: int = 8,
1054
+ num_queries: int = 1,
1055
+ num_blocks: int = 2,
1056
+ attention_dim: int = 512,
1057
+ attention_heads: int = 8,
1058
+ linear_units: int = 2048,
1059
+ dropout_rate: float = 0.0,
1060
+ normalize_before: bool = True,
1061
+ ):
1062
+ super().__init__()
1063
+
1064
+ self.decoders = nn.ModuleList([
1065
+ nn.TransformerDecoderLayer(
1066
+ d_model=attention_dim,
1067
+ nhead=attention_heads,
1068
+ dim_feedforward=linear_units,
1069
+ dropout=dropout_rate,
1070
+ activation="relu",
1071
+ batch_first=True,
1072
+ norm_first=normalize_before, # TODO need to verify
1073
+ ) for _ in range(num_blocks)
1074
+ ])
1075
+
1076
+ self.queries = nn.Parameter(torch.zeros(1, num_queries, attention_dim))
1077
+ self.after_norm = (nn.LayerNorm(attention_dim, eps=1e-12)
1078
+ if normalize_before else None)
1079
+ self.window_size = window_size
1080
+ self.gradient_checkpointing_enable = False
1081
+
1082
+ def enable_gradient_checkpointing(self):
1083
+ self.gradient_checkpointing_enable = True
1084
+
1085
+ def disable_gradient_checkpointing(self):
1086
+ self.gradient_checkpointing_enable = False
1087
+
1088
+ def forward(self, audio_embed, mask, embed_len=None):
1089
+ """forward decoder"""
1090
+ # audio_embed: N x T x D => N x D x T
1091
+
1092
+ audio_embed = audio_embed.transpose(1, 2)
1093
+ # audio_embed: N x D x 1 x T => N x DK x T'
1094
+ padding = audio_embed.shape[-1] % self.window_size
1095
+ if padding > 0:
1096
+ audio_embed = F.pad(audio_embed, (0, self.window_size - padding),
1097
+ "constant", 0)
1098
+
1099
+ embed_chunk = F.unfold(
1100
+ audio_embed[..., None, :],
1101
+ kernel_size=(1, self.window_size),
1102
+ stride=(1, self.window_size),
1103
+ )
1104
+ bsz, _, slen = embed_chunk.shape
1105
+ # N x D x K x T'
1106
+ embed_chunk = embed_chunk.view(bsz, -1, self.window_size, slen)
1107
+ # N x T' x K x D
1108
+ embed_chunk = embed_chunk.transpose(1, 3).contiguous()
1109
+ # NT' x K x D
1110
+ embed_chunk = embed_chunk.view(bsz * slen, self.window_size, -1)
1111
+ # NT' x 1 x D
1112
+ q = self.queries.expand(bsz * slen, -1, -1)
1113
+ for layer in self.decoders:
1114
+ if self.gradient_checkpointing_enable and self.training:
1115
+ q = checkpoint(
1116
+ layer.__call__,
1117
+ q,
1118
+ embed_chunk,
1119
+ None,
1120
+ mask,
1121
+ use_reentrant=True,
1122
+ )
1123
+ else:
1124
+ q = layer(tgt=q,
1125
+ memory=embed_chunk,
1126
+ tgt_mask=None,
1127
+ memory_mask=mask)
1128
+
1129
+ if self.after_norm is not None:
1130
+ q = self.after_norm(q)
1131
+
1132
+ if embed_len is not None:
1133
+ embed_len = embed_len // self.window_size
1134
+ # N x T' x D
1135
+ out = q.view(bsz, slen, -1)
1136
+
1137
+ return out, embed_len
1138
+
1139
+
1140
+class AudioEmbedding(nn.Module):
1141
+ """Image embedding."""
1142
+
1143
+ def __init__(self, config: PretrainedConfig, **kwargs) -> None:
1144
+ super().__init__()
1145
+ self.config = config
1146
+ # n_embed or hidden_size for text LM
1147
+ hidden_size = (config.n_embd
1148
+ if hasattr(config, "n_embd") else config.hidden_size)
1149
+
1150
+ if hasattr(config, "embd_pdrop") or hasattr(config, "embed_pdrop"):
1151
+ embd_drop = (config.embd_pdrop if hasattr(config, "embd_pdrop")
1152
+ else config.embed_pdrop)
1153
+ self.drop = nn.Dropout(embd_drop)
1154
+ else:
1155
+ self.drop = None
1156
+
1157
+ # self.wte = nn.Embedding(config.vocab_size, hidden_size)
1158
+
1159
+ audio_dim_out = (
1160
+ None # Set this variable according to the actual audio processor
1161
+ )
1162
+ self.layer_idx = -2
1163
+
1164
+ if (isinstance(config.audio_processor, dict)
1165
+ and config.audio_processor.get("name", None) == "cascades"):
1166
+ encoder_config = config.audio_processor.get("config", None)
1167
+ assert encoder_config is not None
1168
+ self.encoder = ConformerEncoder(**encoder_config)
1169
+
1170
+ # fake initialization, create encoder_embedding layer only so that
1171
+ # in decoding, all parameters can be loaded in
1172
+ # from_pretrained_function in training, we do post init after
1173
+ # from_pretrained function to make sure the correct initialization
1174
+ self.encoder.post_init({})
1175
+
1176
+ audio_dim_out = encoder_config["attention_dim"]
1177
+ n_mels = encoder_config["input_size"]
1178
+ else:
1179
+ raise NotImplementedError("")
1180
+
1181
+ assert (audio_dim_out
1182
+ is not None), "Remember to set values for audio_dim_out"
1183
+ self.audio_dim_out = audio_dim_out
1184
+ self.audio_dim_in = n_mels
1185
+
1186
+ self.freeze_audio_processor = kwargs.get("freeze_audio_processor",
1187
+ False)
1188
+
1189
+ self.downsample_rate = kwargs.get("downsample_rate", 1)
1190
+
1191
+ if kwargs.get("use_qformer", False):
1192
+ qformer_config = kwargs.get("qformer_config", {})
1193
+ qformer_config["attention_dim"] = audio_dim_out
1194
+ self.qformer = WindowQformer(**qformer_config)
1195
+ else:
1196
+ self.qformer = None
1197
+
1198
+ if kwargs.get("use_conv_downsample", False):
1199
+ assert (self.qformer is None
1200
+ ), "don't support use qformer and conv downsample together"
1201
+ nemo_conv_settings = kwargs.get("nemo_conv_settings", {})
1202
+ default_nemo_conv_settings = {
1203
+ "subsampling": "dw_striding",
1204
+ "subsampling_factor": self.downsample_rate,
1205
+ "feat_in": audio_dim_out,
1206
+ "feat_out": audio_dim_out,
1207
+ "conv_channels": 256,
1208
+ "subsampling_conv_chunking_factor": 1,
1209
+ "activation": nn.ReLU(),
1210
+ "is_causal": False,
1211
+ }
1212
+ # Override any of the defaults with the incoming, user settings
1213
+ if nemo_conv_settings:
1214
+ default_nemo_conv_settings.update(nemo_conv_settings)
1215
+ for i in ["subsampling_factor", "feat_in", "feat_out"]:
1216
+ assert (
1217
+ i not in nemo_conv_settings
1218
+ ), "{i} should be specified outside of the NeMo dictionary"
1219
+
1220
+ self.conv_ds = NemoConvSubsampling(**default_nemo_conv_settings, )
1221
+ else:
1222
+ self.conv_ds = None
1223
+
1224
+ enable_gradient_checkpointing = kwargs.get(
1225
+ "enable_gradient_checkpointing", False)
1226
+ if enable_gradient_checkpointing:
1227
+ self.encoder.gradient_checkpointing_enable()
1228
+
1229
+ if self.qformer:
1230
+ self.qformer.enable_gradient_checkpointing()
1231
+
1232
+ projection_cls = kwargs.get("projection_cls", "linear")
1233
+ if projection_cls == "linear":
1234
+ self.audio_projection = nn.Linear(audio_dim_out, hidden_size)
1235
+ elif projection_cls == "mlp":
1236
+ # follow llava-v1.5's implementation
1237
+ # (do not use image_projection and image_proj_norm)
1238
+ dim_projection = hidden_size
1239
+ depth = 2
1240
+ self.linear_downsample_rate = (1 if (self.qformer or self.conv_ds)
1241
+ else self.downsample_rate)
1242
+ layers = [
1243
+ nn.Linear(audio_dim_out * self.linear_downsample_rate,
1244
+ dim_projection)
1245
+ ]
1246
+ for _ in range(1, depth):
1247
+ layers.extend(
1248
+ [nn.GELU(),
1249
+ nn.Linear(dim_projection, dim_projection)])
1250
+ self.audio_projection = nn.Sequential(*layers)
1251
+ # NOTE vision-speech tasks use a separate projection layer
1252
+ layers = [
1253
+ nn.Linear(audio_dim_out * self.linear_downsample_rate,
1254
+ dim_projection)
1255
+ ]
1256
+ for _ in range(1, depth):
1257
+ layers.extend(
1258
+ [nn.GELU(),
1259
+ nn.Linear(dim_projection, dim_projection)])
1260
+ self.audio_projection_for_vision = nn.Sequential(*layers)
1261
+ else:
1262
+ raise NotImplementedError(
1263
+ f"projection_cls = {projection_cls}, not implemented")
1264
+
1265
+ # TODO: audio sequence compression - Qformer
1266
+ self.vocab_size = config.vocab_size
1267
+ self.input_embeds = None
1268
+ self.audio_embed_sizes = None
1269
+
1270
+ def set_audio_embeds(self, input_embeds: torch.FloatTensor) -> None:
1271
+ self.input_embeds = input_embeds
1272
+
1273
+ def set_audio_embed_sizes(self,
1274
+ audio_embed_sizes: torch.LongTensor) -> None:
1275
+ self.audio_embed_sizes = audio_embed_sizes
1276
+
1277
+ def get_audio_features(
1278
+ self,
1279
+ input_embeds: torch.FloatTensor,
1280
+ audio_attention_mask: torch.Tensor = None,
1281
+ audio_projection_mode: str = "speech",
1282
+ ):
1283
+
1284
+ if self.freeze_audio_processor:
1285
+ with torch.no_grad():
1286
+ audio_features, masks = self.encoder(input_embeds,
1287
+ audio_attention_mask)
1288
+ else:
1289
+ audio_features, masks = self.encoder(input_embeds,
1290
+ audio_attention_mask)
1291
+
1292
+ if self.qformer is not None:
1293
+ audio_features, _ = self.qformer(audio_features, mask=None)
1294
+
1295
+ if self.conv_ds is not None:
1296
+ if masks is not None:
1297
+ masks = masks.squeeze(1)
1298
+
1299
+ audio_features, masks = self.conv_ds(audio_features, mask=masks)
1300
+
1301
+ if self.linear_downsample_rate != 1:
1302
+ bs, seq_len, feat_dim = audio_features.size()
1303
+ padding = seq_len % self.linear_downsample_rate
1304
+ if padding > 0:
1305
+ audio_features = F.pad(
1306
+ audio_features,
1307
+ (0, 0, 0, self.linear_downsample_rate - padding),
1308
+ "constant",
1309
+ 0,
1310
+ )
1311
+
1312
+ seq_len = audio_features.size(1)
1313
+ audio_features = audio_features.view(
1314
+ bs,
1315
+ seq_len // self.linear_downsample_rate,
1316
+ feat_dim * self.linear_downsample_rate,
1317
+ )
1318
+
1319
+ if audio_projection_mode == 'speech':
1320
+ audio_set_tensor = self.audio_projection(audio_features)
1321
+ elif audio_projection_mode == 'vision':
1322
+ audio_set_tensor = self.audio_projection_for_vision(audio_features)
1323
+ else:
1324
+ raise ValueError(
1325
+ f"audio_projection_mode = {audio_projection_mode} not "\
1326
+ "implemented"
1327
+ )
1328
+
1329
+ return audio_set_tensor
1330
+
1331
+ def forward(
1332
+ self,
1333
+ input_ids: torch.LongTensor,
1334
+ input_embeds: torch.FloatTensor,
1335
+ audio_embed_sizes,
1336
+ **kwargs,
1337
+ ) -> torch.FloatTensor:
1338
+ """
1339
+ arguments:
1340
+ input_ids: input text ids (B, U)
1341
+ input_embeds: audio features (B, T, D) B: num audios in a sequence
1342
+ """
1343
+ assert input_embeds is not None and len(input_embeds) == len(
1344
+ audio_embed_sizes)
1345
+
1346
+ input_shape = input_ids.size()
1347
+ input_ids = input_ids.view(-1, input_shape[-1])
1348
+
1349
+ with torch.no_grad():
1350
+ positions = (input_ids == _AUDIO_PLACEHOLDER_TOKEN_ID).nonzero(
1351
+ as_tuple=False)
1352
+
1353
+ if not isinstance(input_embeds, list):
1354
+ input_embeds = [input_embeds]
1355
+
1356
+ audio_projection_mode = kwargs.get("audio_projection_mode", "speech")
1357
+ audio_set_tensor = [
1358
+ self.get_audio_features(
1359
+ input_embed, audio_projection_mode=audio_projection_mode)
1360
+ for input_embed in input_embeds
1361
+ ]
1362
+
1363
+ with torch.no_grad():
1364
+ input_ids.clamp_min_(0).clamp_max_(self.vocab_size)
1365
+
1366
+ if "wte" in kwargs:
1367
+ # we use the token embedding layer from the huggingface model, this
1368
+ # is REQUIRED to make sure we are using the loaded weights.
1369
+ hidden_states = kwargs["wte"](input_ids)
1370
+ else:
1371
+ # otherwise, we use token embedding in pretrained mixformer from
1372
+ # phi team
1373
+ hidden_states = self.wte(input_ids)
1374
+
1375
+ if len(positions.tolist()) > 0:
1376
+ assert sum(audio_embed_sizes) == len(
1377
+ positions
1378
+ ), "please ensure the encoder outputs have the same length as"\
1379
+ " defined in input_ids!"
1380
+ idx = 0
1381
+ for i in range(len(audio_embed_sizes)):
1382
+ cnt = audio_embed_sizes[i]
1383
+ assert audio_set_tensor[i].shape[0] == 1
1384
+ hidden_states[
1385
+ positions[idx, 0],
1386
+ positions[idx, 1]:positions[idx, 1] + cnt,
1387
+ ] = (audio_set_tensor[i][0, :audio_embed_sizes[i], :].to(
1388
+ hidden_states.dtype).to(hidden_states.device))
1389
+ idx += cnt
1390
+
1391
+ else:
1392
+ if self.training:
1393
+ # hidden_states[:, 0:img_set_tensor.shape[0]] =
1394
+ # hidden_states[:, 0:img_set_tensor.shape[0]] +
1395
+ # 0 * img_set_tensor.to(hidden_states.dtype)
1396
+ # .to(hidden_states.device)
1397
+ hidden_states[:, 0:1] = hidden_states[:, 0:1] + \
1398
+ 0 * audio_set_tensor[:, 0:1].to(hidden_states.dtype)\
1399
+ .to(hidden_states.device)
1400
+
1401
+ if self.drop is not None:
1402
+ hidden_states = self.drop(hidden_states)
1403
+ return hidden_states
vllm/model_executor/models/phi4mm_utils.py
new
+1969
@@ -0,0 +1,1969 @@
1
+# SPDX-License-Identifier: Apache-2.0
2
+# Copyright (c) Microsoft Corporation.
3
+# Licensed under the MIT license.
4
+# Code copied from Microsoft/MoE by Jacob Platin (jacobplatin@microsoft.com)
5
+# but implemented by the Phi-Speech team
6
+#!/usr/bin/env python3
7
+import math
8
+from functools import partial
9
+from typing import Callable, Dict, Optional, Tuple, Union
10
+
11
+import torch
12
+import torch.nn.functional as F
13
+from torch import Tensor, nn
14
+from torch.distributed.algorithms._checkpoint.checkpoint_wrapper import (
15
+ CheckpointImpl, checkpoint_wrapper, offload_wrapper)
16
+
17
+
18
+class Block(nn.Module):
19
+ """Block abstract module"""
20
+
21
+ def __init__(self, input_size, output_size):
22
+ super().__init__()
23
+ self.input_size = input_size
24
+ self.output_size = output_size
25
+
26
+
27
+def get_activation(name="relu"):
28
+ """Select an activation function by name
29
+
30
+ Args:
31
+ name: str
32
+ activation function name,
33
+ one of ["relu", "gelu", "swish", "sigmoid"],
34
+ default "relu".
35
+ """
36
+ name = name.lower()
37
+ if name == "relu":
38
+ return nn.ReLU(inplace=True)
39
+ if name == "gelu":
40
+ return nn.GELU()
41
+ if name == "swish":
42
+ return Swish()
43
+ if name == "sigmoid":
44
+ return torch.nn.Sigmoid()
45
+ return nn.Identity()
46
+
47
+
48
+def adaptive_enc_mask(x_len, chunk_start_idx, left_window=0, right_window=0):
49
+ """
50
+ The function is very important for Transformer Transducer Streaming mode
51
+ Args:
52
+ xs_len (int): sequence length
53
+ chunk_start_idx (list): first idx of each chunk, such as [0,18,36,48].
54
+ It also supports adaptive chunk size [0,10,15,45]
55
+ left_window (int): how many left chunks can be seen
56
+ right_window (int): how many right chunks can be seen. It is used for
57
+ chunk overlap model.
58
+ Returns:
59
+ mask (torch.Tensor): a mask tensor for streaming model
60
+ Torch 1.0.1
61
+ tensor([[1., 1., 0., 0.],
62
+ [0., 1., 1., 0.],
63
+ [0., 0., 1., 1.]])
64
+ Torch 1.4.1
65
+ tensor([[True., True., False., False.],
66
+ [False., True., True., False.],
67
+ [False., False., True., True.]])
68
+ """
69
+ chunk_start_idx = torch.Tensor(chunk_start_idx).long(
70
+ ) # first idx of each chunk, such as [0,18,36,48].
71
+ start_pad = torch.nn.functional.pad(
72
+ chunk_start_idx,
73
+ (1, 0)) # append 0 to the beginning, so it becomes [0, 0, 18, 36, 48]
74
+ end_pad = torch.nn.functional.pad(
75
+ chunk_start_idx, (0, 1), value=x_len
76
+ ) # append x_len to the end, so it becomes [0,18,36,48, x_len]
77
+ seq_range = torch.arange(0,
78
+ x_len).unsqueeze(-1) # seq_range size: [x_len, 1]
79
+ idx = ((seq_range < end_pad) &
80
+ (seq_range >= start_pad)).nonzero()[:, 1] # idx size: [x_len]
81
+ # boundary = end_pad[idx] # boundary size: [x_len]
82
+ seq_range_expand = (torch.arange(0, x_len).unsqueeze(0).expand(x_len, -1)
83
+ ) # seq_range_expand size [x_len, x_len]
84
+ idx_left = idx - left_window
85
+ idx_left[idx_left < 0] = 0
86
+ boundary_left = start_pad[idx_left]
87
+ mask_left = seq_range_expand >= boundary_left.unsqueeze(-1)
88
+ idx_right = idx + right_window
89
+ idx_right[idx_right > len(chunk_start_idx)] = len(chunk_start_idx)
90
+ boundary_right = end_pad[idx_right]
91
+ mask_right = seq_range_expand < boundary_right.unsqueeze(-1)
92
+ return mask_left & mask_right
93
+
94
+
95
+class Swish(nn.Module):
96
+ """Implement Swish activation module.
97
+ From https://arxiv.org/pdf/2005.03191.pdf
98
+
99
+ """
100
+
101
+ def __init__(self) -> None:
102
+ super().__init__()
103
+ self.act_fn = nn.Sigmoid()
104
+
105
+ def forward(self, x: Tensor) -> Tensor:
106
+ """Apply Swish function
107
+
108
+ Args:
109
+ x: torch.Tensor
110
+ Input.
111
+ """
112
+ return x * self.act_fn(x)
113
+
114
+
115
+class GLU(nn.Module):
116
+ """Implement Gated Linear Unit (GLU) module"""
117
+
118
+ def __init__(self, dim: int = -1, act_name: str = "sigmoid") -> None:
119
+ super().__init__()
120
+ self.dim = dim
121
+ self.act_name = act_name.lower()
122
+
123
+ if self.act_name == "relu":
124
+ self.act_fn = nn.ReLU(inplace=True)
125
+ elif self.act_name == "gelu":
126
+ self.act_fn = nn.GELU()
127
+ elif self.act_name == "swish":
128
+ self.act_fn = Swish()
129
+ elif self.act_name == "sigmoid":
130
+ self.act_fn = nn.Sigmoid()
131
+ else:
132
+ self.act_fn = nn.Identity()
133
+
134
+ def forward(self, x: Tensor) -> Tensor:
135
+ """GLU forward
136
+ Apply Swish function on the first half of input matrices
137
+ with sigmoid of the second half.
138
+
139
+ Args:
140
+ x: torch.Tensor
141
+ Input.
142
+
143
+ """
144
+ half_x, gate = x.chunk(2, dim=self.dim)
145
+ return half_x * self.act_fn(gate)
146
+
147
+
148
+# TODO: Abdel, this can be improved using GLU module
149
+class GLUPointWiseConv(nn.Module):
150
+ """GLUPointWiseConv module
151
+ used for conformer architecture,
152
+ for more details see:
153
+ https://arxiv.org/pdf/2005.08100v1.pdf
154
+
155
+ Args:
156
+ input_dim: int
157
+ input channel size.
158
+ output_dim: int
159
+ output channel size.
160
+ kernel_size: int
161
+ kernel size
162
+ glu_type: str, optional
163
+ activation function one of
164
+ ["sigmoid", "relu", "gelu"]
165
+ default "sigmoid".
166
+ bias_in_glu: bool, optional
167
+ use addtive bias in glu
168
+ causal: bool, optional
169
+ if set to True, padding is set to the half of
170
+ kernel size, ie, convolution can't see future frames.
171
+ default False.
172
+
173
+ """
174
+
175
+ def __init__(
176
+ self,
177
+ input_dim,
178
+ output_dim,
179
+ kernel_size,
180
+ glu_type="sigmoid",
181
+ bias_in_glu=True,
182
+ causal=False,
183
+ ):
184
+ super().__init__()
185
+
186
+ self.glu_type = glu_type
187
+ self.output_dim = output_dim
188
+ self.bias_in_glu = bias_in_glu
189
+ if causal:
190
+ self.ext_pw_conv_1d = nn.Conv1d(
191
+ input_dim,
192
+ output_dim * 2,
193
+ kernel_size,
194
+ 1,
195
+ padding=(kernel_size - 1),
196
+ )
197
+ else:
198
+ self.ext_pw_conv_1d = nn.Conv1d(
199
+ input_dim,
200
+ output_dim * 2,
201
+ kernel_size,
202
+ 1,
203
+ padding=(kernel_size - 1) // 2,
204
+ )
205
+
206
+ if glu_type == "sigmoid":
207
+ self.glu_act = nn.Sigmoid()
208
+ elif glu_type == "relu":
209
+ self.glu_act = nn.ReLU()
210
+ elif glu_type == "gelu":
211
+ self.glu_act = nn.GELU()
212
+ elif glu_type == "swish":
213
+ self.glu_act = Swish()
214
+ else:
215
+ raise ValueError(f"Unsupported activation type {self.glu_act}")
216
+
217
+ if bias_in_glu:
218
+ self.b1 = nn.Parameter(torch.zeros(1, output_dim, 1))
219
+ self.b2 = nn.Parameter(torch.zeros(1, output_dim, 1))
220
+
221
+ def forward(self, x):
222
+ """
223
+ Args:
224
+ x: torch.Tensor
225
+ input tensor
226
+ """
227
+ # to be consistent with GLULinear, we assume the input always has the
228
+ # #channel (#dim) in the last dimension of the tensor, so need to
229
+ # switch the dimension first for 1D-Conv case
230
+ x = x.permute([0, 2, 1])
231
+ x = self.ext_pw_conv_1d(x)
232
+ if self.glu_type == "bilinear":
233
+ if self.bias_in_glu:
234
+ x = (x[:, 0:self.output_dim, :] + self.b1) * (
235
+ x[:, self.output_dim:self.output_dim * 2, :] + self.b2)
236
+ else:
237
+ x = (x[:, 0:self.output_dim, :]) * (
238
+ x[:, self.output_dim:self.output_dim * 2, :])
239
+ else:
240
+ if self.bias_in_glu:
241
+ x = (x[:, 0:self.output_dim, :] + self.b1) * self.glu_act(
242
+ x[:, self.output_dim:self.output_dim * 2, :] + self.b2)
243
+ else:
244
+ x = (x[:, 0:self.output_dim, :]) * self.glu_act(
245
+ x[:, self.output_dim:self.output_dim * 2, :])
246
+
247
+ x = x.permute([0, 2, 1])
248
+ return x
249
+
250
+
251
+class DepthWiseSeperableConv1d(nn.Module):
252
+ """DepthWiseSeperableConv1d module used in Convnet module
253
+ for the conformer, for more details see:
254
+ https://arxiv.org/pdf/2005.08100v1.pdf
255
+
256
+ Args:
257
+ input_dim: int
258
+ input channel size.
259
+ depthwise_seperable_out_channel: int
260
+ if set different to 0, the number of
261
+ depthwise_seperable_out_channel will be used as a channel_out
262
+ of the second conv1d layer.
263
+ otherwise, it equal to 0, the second conv1d layer is skipped.
264
+ kernel_size: int
265
+ kernel_size
266
+ depthwise_multiplier: int
267
+ number of input_dim channels duplication. this value
268
+ will be used to compute the hidden channels of the Conv1D.
269
+ padding: int, optional
270
+ padding for the conv1d,
271
+ default: 0.
272
+
273
+ """
274
+
275
+ def __init__(
276
+ self,
277
+ input_dim,
278
+ depthwise_seperable_out_channel,
279
+ kernel_size,
280
+ depthwise_multiplier,
281
+ padding=0,
282
+ ):
283
+ super().__init__()
284
+
285
+ self.dw_conv = nn.Conv1d(
286
+ input_dim,
287
+ input_dim * depthwise_multiplier,
288
+ kernel_size,
289
+ 1,
290
+ padding=padding,
291
+ groups=input_dim,
292
+ )
293
+
294
+ if depthwise_seperable_out_channel != 0:
295
+ self.pw_conv = nn.Conv1d(
296
+ input_dim * depthwise_multiplier,
297
+ depthwise_seperable_out_channel,
298
+ 1,
299
+ 1,
300
+ 0,
301
+ )
302
+ else:
303
+ self.pw_conv = nn.Identity()
304
+ self.depthwise_seperable_out_channel = depthwise_seperable_out_channel
305
+
306
+ def forward(self, x):
307
+ """
308
+
309
+ Args:
310
+ x: torch.Tensor
311
+ input tensor
312
+ """
313
+ x = self.dw_conv(x)
314
+ if self.depthwise_seperable_out_channel != 0:
315
+ x = self.pw_conv(x)
316
+ return x
317
+
318
+
319
+class ConvModule(nn.Module):
320
+ """ConvModule Module for the conformer block.
321
+ for more details see:
322
+ https://arxiv.org/pdf/2005.08100v1.pdf
323
+
324
+ Args:
325
+ input_dim: int
326
+ input channel size.
327
+ ext_pw_out_channel: int
328
+ if > 0, ext_pw_out_channel is a dim channel size
329
+ for the last pointwise conv after swish activation.
330
+ depthwise_seperable_out_channel: int
331
+ if set different to 0, the number of
332
+ depthwise_seperable_out_channel
333
+ will be used as a channel_out of the second conv1d layer.
334
+ otherwise, it equal to 0, the second conv1d layer is skipped.
335
+ ext_pw_kernel_size: int
336
+ kernel size of the conv pointwise of the conformer.
337
+ kernel_size: int
338
+ kernel size.
339
+ depthwise_multiplier: int
340
+ number of input_dim channels duplication. this value
341
+ will be used to compute the hidden channels of the Conv1D.
342
+ dropout_rate: float
343
+ dropout rate.
344
+ causal: bool, optional
345
+ if set to True, convolution have no access
346
+ to future frames. default False.
347
+ batch_norm: bool, optional
348
+ if set to True, apply batchnorm before activation.
349
+ default False
350
+ chunk_se: int, optional
351
+ 0 for offline SE.
352
+ 1 for streaming SE, where mean is computed
353
+ by accumulated history until current chunk_se.
354
+ 2 for streaming SE, where mean is computed
355
+ by only the current chunk.
356
+ chunk_size: int, optional
357
+ chunk size for cnn. default 18
358
+ activation: str, optional
359
+ activation function used in ConvModule,
360
+ default: "relu".
361
+ glu_type: str, optional
362
+ activation function used for the glu,
363
+ default: "sigmoid".
364
+ bias_in_glu: bool, optional
365
+ if set to True, use additive bias in the weight module
366
+ before GLU.
367
+ linear_glu_in_convm: bool, optional
368
+ if set to True, use GLULinear module,
369
+ otherwise, used GLUPointWiseConv module.
370
+ default to False.
371
+ export: bool, optional,
372
+ if set to True, padding is equal to 0. This is for inference,
373
+ or onnx export. Typically this is set by the export program or
374
+ the decoder program, and it isn't present in your config file.
375
+ default False
376
+ """
377
+
378
+ def __init__(
379
+ self,
380
+ input_dim,
381
+ ext_pw_out_channel,
382
+ depthwise_seperable_out_channel,
383
+ ext_pw_kernel_size,
384
+ kernel_size,
385
+ depthwise_multiplier,
386
+ dropout_rate,
387
+ causal=False,
388
+ batch_norm=False,
389
+ chunk_se=0,
390
+ chunk_size=18,
391
+ activation="relu",
392
+ glu_type="sigmoid",
393
+ bias_in_glu=True,
394
+ linear_glu_in_convm=False,
395
+ export=False,
396
+ ):
397
+ super().__init__()
398
+ self.layer_norm = nn.LayerNorm(input_dim)
399
+ self.input_dim = input_dim
400
+ self.ext_pw_out_channel = ext_pw_out_channel
401
+ self.ext_pw_kernel_size = ext_pw_kernel_size
402
+ self.depthwise_seperable_out_channel = depthwise_seperable_out_channel
403
+ self.glu_type = glu_type
404
+ self.bias_in_glu = bias_in_glu
405
+ self.linear_glu_in_convm = linear_glu_in_convm
406
+ self.causal = causal
407
+
408
+ self._add_ext_pw_layer()
409
+
410
+ self.batch_norm = batch_norm
411
+ self.kernel_size = kernel_size
412
+
413
+ if batch_norm:
414
+ self.bn_layer = nn.BatchNorm1d(input_dim)
415
+
416
+ self.act = get_activation(activation)
417
+ self.dropout = nn.Dropout(dropout_rate)
418
+ self.export = export
419
+
420
+ if causal:
421
+ padding = 0 if export else kernel_size - 1
422
+ else:
423
+ padding = (kernel_size - 1) // 2
424
+
425
+ self.dw_sep_conv_1d = DepthWiseSeperableConv1d(
426
+ input_dim,
427
+ depthwise_seperable_out_channel,
428
+ kernel_size,
429
+ depthwise_multiplier,
430
+ padding=padding,
431
+ )
432
+
433
+ if depthwise_seperable_out_channel != 0:
434
+ if input_dim != depthwise_seperable_out_channel:
435
+ self.ln2 = nn.Linear(depthwise_seperable_out_channel,
436
+ input_dim)
437
+ else:
438
+ if depthwise_multiplier != 1:
439
+ self.ln2 = nn.Linear(input_dim * depthwise_multiplier,
440
+ input_dim)
441
+
442
+ def _add_ext_pw_layer(self):
443
+ """
444
+ This function is an extension of __init__ function
445
+ and dedicated to the convolution module creation
446
+ of the conformer.
447
+ """
448
+ self.ln1 = self.glu = self.bn_layer = self.ext_pw_conv_1d = (
449
+ nn.Identity()) # jit hacks.
450
+ self.squeeze_excitation = nn.Identity() # jit.
451
+ self.apply_ln1 = self.fix_len1 = False # jit.
452
+
453
+ if self.ext_pw_out_channel != 0:
454
+ if self.causal:
455
+ self.ext_pw_conv_1d = nn.Conv1d(
456
+ self.input_dim,
457
+ self.ext_pw_out_channel,
458
+ self.ext_pw_kernel_size,
459
+ 1,
460
+ padding=(self.ext_pw_kernel_size - 1),
461
+ )
462
+ if self.ext_pw_kernel_size > 1:
463
+ self.fix_len1 = True
464
+ else:
465
+ self.fix_len1 = False
466
+ else:
467
+ self.ext_pw_conv_1d = nn.Conv1d(
468
+ self.input_dim,
469
+ self.ext_pw_out_channel,
470
+ self.ext_pw_kernel_size,
471
+ 1,
472
+ padding=(self.ext_pw_kernel_size - 1) // 2,
473
+ )
474
+ self.fix_len1 = False
475
+
476
+ if self.linear_glu_in_convm:
477
+ self.glu = GLULinear(
478
+ self.input_dim,
479
+ self.ext_pw_out_channel,
480
+ self.glu_type,
481
+ self.bias_in_glu,
482
+ )
483
+ else:
484
+ self.glu = GLUPointWiseConv(
485
+ self.input_dim,
486
+ self.ext_pw_out_channel,
487
+ self.ext_pw_kernel_size,
488
+ self.glu_type,
489
+ self.bias_in_glu,
490
+ self.causal,
491
+ )
492
+
493
+ if self.input_dim != self.ext_pw_out_channel:
494
+ self.apply_ln1 = True
495
+ self.ln1 = nn.Linear(self.ext_pw_out_channel, self.input_dim)
496
+ else:
497
+ self.apply_ln1 = False
498
+ else:
499
+ self.pw_conv_simplify_w = torch.nn.Parameter(torch.ones(3))
500
+ self.pw_conv_simplify_b = torch.nn.Parameter(torch.zeros(3))
501
+
502
+ def forward(self, x):
503
+ """ConvModule Forward.
504
+
505
+ Args:
506
+ x: torch.Tensor
507
+ input tensor.
508
+ """
509
+ x = self.layer_norm(x)
510
+
511
+ if self.ext_pw_out_channel != 0:
512
+ x = self.glu(x)
513
+ if self.causal and self.ext_pw_kernel_size > 1:
514
+ x = x[:, :-(self.ext_pw_kernel_size - 1), :]
515
+ if self.apply_ln1:
516
+ x = self.ln1(x)
517
+ else:
518
+ x_0 = x * self.pw_conv_simplify_w[0] + self.pw_conv_simplify_b[0]
519
+ x_1 = x * self.pw_conv_simplify_w[1] + self.pw_conv_simplify_b[1]
520
+ x = x_0 + x_1
521
+
522
+ x = x.permute([0, 2, 1])
523
+
524
+ x = self.dw_sep_conv_1d(x)
525
+ if self.causal and self.kernel_size > 1:
526
+ x = x[:, :, :-(self.kernel_size - 1)]
527
+ if hasattr(self, "ln2"):
528
+ x = x.permute([0, 2, 1])
529
+ x = self.ln2(x)
530
+ x = x.permute([0, 2, 1])
531
+ if self.batch_norm:
532
+ x = self.bn_layer(x)
533
+ x = self.act(x)
534
+
535
+ if self.ext_pw_out_channel != 0:
536
+ x = self.ext_pw_conv_1d(x)
537
+ if self.fix_len1:
538
+ x = x[:, :, :-(self.ext_pw_kernel_size - 1)]
539
+
540
+ if self.apply_ln1:
541
+ x = x.permute([0, 2, 1])
542
+ x = self.ln1(x)
543
+ x = x.permute([0, 2, 1])
544
+
545
+ x = x.permute([0, 2, 1])
546
+ else:
547
+ x = x.unsqueeze(1).permute([0, 1, 3, 2])
548
+ x = x * self.pw_conv_simplify_w[2] + self.pw_conv_simplify_b[2]
549
+ x = x.squeeze(1)
550
+
551
+ x = self.dropout(x)
552
+ return x
553
+
554
+
555
+class GLULinear(nn.Module):
556
+ """Linear + GLU module
557
+
558
+ Args:
559
+ input_dim: int
560
+ input size
561
+ output_dim: int
562
+ output size.
563
+ glu_type:
564
+ activation function name used in glu module.
565
+ default "sigmoid" (swish function).
566
+ bias_in_glu: bool, optional
567
+ If True, the addtive bias is added. Default False.
568
+ """
569
+
570
+ def __init__(
571
+ self,
572
+ input_dim,
573
+ output_dim,
574
+ glu_type="sigmoid",
575
+ bias_in_glu=True,
576
+ ):
577
+ super().__init__()
578
+ self.linear = nn.Linear(input_dim, output_dim * 2, bias_in_glu)
579
+ self.glu_act = GLU(-1, glu_type)
580
+
581
+ def forward(self, x):
582
+ """GLULinear forward
583
+
584
+ Args:
585
+ x: torch.Tensor
586
+ inpute tensor.
587
+ """
588
+ x = self.linear(x)
589
+ return self.glu_act(x)
590
+
591
+
592
+class FeedForward(nn.Module):
593
+ """FeedForward Module.
594
+ For more details see Conformer paper:
595
+ https://arxiv.org/pdf/2005.08100.pdf
596
+
597
+ Args:
598
+ d_model: int
599
+ input size.
600
+ d_inner: int
601
+ output size.
602
+ dropout_rate: float,
603
+ dropout rate.
604
+ activation: str,
605
+ activation function name,
606
+ one of ["relu", "swish", "sigmoid"],
607
+ sigmoid activation is only used with "glu_in_fnn=True",
608
+ default "sigmoid".
609
+ bias_in_glu: bool, optional
610
+ """
611
+
612
+ def __init__(
613
+ self,
614
+ d_model,
615
+ d_inner,
616
+ dropout_rate,
617
+ activation="sigmoid",
618
+ bias_in_glu=True,
619
+ ):
620
+ super().__init__()
621
+ self.d_model = d_model
622
+ self.d_inner = d_inner
623
+
624
+ self.layer_norm = nn.LayerNorm(d_model)
625
+ module = GLULinear(d_model, d_inner, activation, bias_in_glu)
626
+ self.net = nn.Sequential(
627
+ module,
628
+ nn.Dropout(dropout_rate),
629
+ nn.Linear(d_inner, d_model),
630
+ nn.Dropout(dropout_rate),
631
+ )
632
+
633
+ def forward(self, x):
634
+ """FeedForward forward function.
635
+
636
+ Args:
637
+ x: torch.Tensor
638
+ input tensor.
639
+ """
640
+ out = self.net(self.layer_norm(x))
641
+
642
+ return out
643
+
644
+
645
+#### positional encoding starts here
646
+def _pre_hook(
647
+ state_dict,
648
+ prefix,
649
+ local_metadata,
650
+ strict,
651
+ missing_keys,
652
+ unexpected_keys,
653
+ error_msgs,
654
+):
655
+ """Perform pre-hook in load_state_dict for backward compatibility.
656
+
657
+ Note:
658
+ We saved self.pe until v.0.5.2 but we have omitted it later.
659
+ Therefore, we remove the item "pe" from `state_dict` for backward
660
+ compatibility.
661
+
662
+ """
663
+ k = prefix + "pe"
664
+ if k in state_dict:
665
+ state_dict.pop(k)
666
+
667
+
668
+class T5RelativeAttentionLogitBias(nn.Module):
669
+ """
670
+ This module implements the relative position bias described in Section
671
+ 2.1 of the T5 paper: https://arxiv.org/pdf/1910.10683.pdf
672
+
673
+ The Huggingface implementation is used as a reference
674
+ https://github.com/huggingface/transformers/blob/v4.30.0/src/
675
+ transformers/models/t5/modeling_t5.py#L435
676
+
677
+ Modifies attention as Q*K^T + B, where B is a learned scalar bias based
678
+ on relative position of the query and key. It is HxNxN, where H is the
679
+ number of heads, N is the sequence length.
680
+
681
+ I've made these modifications to the original T5 bias:
682
+ - Skipping of the bucketing step. Original T5 bias converted rel
683
+ position distances into logarithmically increasing buckets. This is
684
+ supposed to help with length generalization.
685
+ - I just directly use rel position index as bias values, as we don't
686
+ need length generalization (40s max is good enough for ASR encoder),
687
+ and it keeps ONNX export simple.
688
+ - I've also extended it so that biases can be asymmetric, the default
689
+ implementation treats L->R and R->L the same. Asymmetric was found to
690
+ yield better results in my experiments.
691
+
692
+ Args:
693
+ num_heads: int
694
+ Number of attention heads
695
+ num_buckets: int
696
+ Number of buckets to use for relative attention bias. This is the
697
+ size of the learnable bias parameter. Bucketing is not yet
698
+ supported, so this defaults to -1 which means no bucketing is
699
+ used (max_distance determines size of bias param).
700
+ max_distance: int
701
+ Maximum distance to use for relative attention bias. With
702
+ num_buckets=-1, this directly controls the max size of the bias
703
+ parameter. When num_buckets > 0 is supported, this will control
704
+ the maximum distance for logarithmic bucketing after which all
705
+ positions are in the same bucket.
706
+ symmetric: bool
707
+ Whether to use symmetric or asymmetric biases. symmetric=False uses
708
+ 2x number of bias params to distinguish L->R from R->L. This was
709
+ found to be better for the encoder.
710
+ """
711
+
712
+ def __init__(self,
713
+ num_heads,
714
+ num_buckets=-1,
715
+ max_distance=1000,
716
+ symmetric=False):
717
+ super().__init__()
718
+ self.num_heads = num_heads
719
+ self.num_buckets = num_buckets
720
+ self.max_distance = max_distance
721
+ self.symmetric = symmetric
722
+ self._skip_bucketing = self.num_buckets < 0
723
+ if self._skip_bucketing:
724
+ self.num_buckets = max_distance
725
+ else:
726
+ raise NotImplementedError(
727
+ "T5 attention bias with bucketed positions is not yet tested")
728
+ if not self.symmetric:
729
+ self.num_buckets *= 2
730
+ self.bias_values = nn.Embedding(self.num_buckets, self.num_heads)
731
+
732
+ def forward(self, x):
733
+ # instantiate bias compatible with shape of x
734
+ maxpos = x.size(1)
735
+ context_position = torch.arange(maxpos,
736
+ device=x.device,
737
+ dtype=torch.long)[:, None]
738
+ memory_position = torch.arange(maxpos,
739
+ device=x.device,
740
+ dtype=torch.long)[None, :]
741
+ relative_position = memory_position - context_position
742
+ # clipping to a maximum distance using ops that play well with ONNX
743
+ # export
744
+ relative_position = relative_position.masked_fill(
745
+ relative_position < -self.max_distance, -self.max_distance)
746
+ relative_position = relative_position.masked_fill(
747
+ relative_position > self.max_distance - 1, self.max_distance - 1)
748
+
749
+ # mapping from relative position to index in the bias parameter
750
+ if self._skip_bucketing:
751
+ bias_idx = relative_position
752
+ else:
753
+ bias_idx = self._bucket_relative_position(relative_position)
754
+ if self.symmetric:
755
+ bias_idx = bias_idx.abs()
756
+ else:
757
+ bias_idx += self.num_buckets // 2
758
+
759
+ t5_rel_att_bias = self.bias_values(bias_idx) # [L, L, H]
760
+ t5_rel_att_bias = t5_rel_att_bias.permute(2, 0, 1).unsqueeze(
761
+ 0) # [1, H, L, L]
762
+
763
+ return t5_rel_att_bias
764
+
765
+ def _bucket_relative_position(self, relative_position):
766
+ # this is a placeholder (isn't tested, likely buggy) using HuggingFace
767
+ # implem as a reference this also needs to be extended to support
768
+ # asymmetric +/- ve positions
769
+ relative_buckets = 0
770
+ if not self.causal:
771
+ self.num_buckets //= 2
772
+ relative_buckets += (relative_position > 0).to(
773
+ torch.long) * self.num_buckets
774
+ relative_position = torch.abs(relative_position)
775
+ else:
776
+ relative_position = -torch.min(relative_position,
777
+ torch.zeros_like(relative_position))
778
+ # now relative_position is in the range [0, inf)
779
+
780
+ # half of the buckets are for exact increments in positions
781
+ max_exact = self.num_buckets // 2
782
+ is_small = relative_position < max_exact
783
+
784
+ # The other half of the buckets are for logarithmically bigger bins in
785
+ # positions up to max_distance
786
+ relative_position_if_large = max_exact + (
787
+ torch.log(relative_position.float() / max_exact) /
788
+ math.log(self.max_distance / max_exact) *
789
+ (self.num_buckets - max_exact)).to(torch.long)
790
+ relative_position_if_large = torch.min(
791
+ relative_position_if_large,
792
+ torch.full_like(relative_position_if_large, self.num_buckets - 1),
793
+ )
794
+
795
+ relative_buckets += torch.where(is_small, relative_position,
796
+ relative_position_if_large)
797
+ return relative_buckets
798
+
799
+
800
+class AbsolutePositionalEncoding(nn.Module):
801
+ """Absolute Positional encoding module.
802
+ This module implement Absolute sinusoidal positional encoding
803
+ from: https://arxiv.org/pdf/1706.03762.pdf
804
+
805
+ Args:
806
+ d_model: int
807
+ Input embedding size.
808
+ dropout_rate: float
809
+ dropout rate
810
+ max_len: int, optional
811
+ Maximum input length sequence, Default 5000
812
+
813
+ """
814
+
815
+ def __init__(self, d_model, dropout_rate, max_len=5000):
816
+ """Construct an PositionalEncoding object."""
817
+ super().__init__()
818
+ self.d_model = d_model
819
+ self.xscale = math.sqrt(self.d_model)
820
+ self.dropout = torch.nn.Dropout(p=dropout_rate)
821
+ self.pe = None
822
+ self.extend_pe(torch.tensor(0.0).expand(1, max_len))
823
+ self._register_load_state_dict_pre_hook(_pre_hook)
824
+
825
+ def extend_pe(self, x):
826
+ """Reset the positional encodings.
827
+
828
+ Args:
829
+ x: torch.Tensor
830
+ """
831
+ if self.pe is not None and self.pe.size(1) >= x.size(1):
832
+ if self.pe.dtype != x.dtype or self.pe.device != x.device:
833
+ self.pe = self.pe.to(dtype=x.dtype, device=x.device)
834
+ return
835
+ pe = torch.zeros(x.size(1), self.d_model)
836
+ position = torch.arange(0, x.size(1), dtype=torch.float32).unsqueeze(1)
837
+ div_term = torch.exp(
838
+ torch.arange(0, self.d_model, 2, dtype=torch.float32) *
839
+ -(math.log(10000.0) / self.d_model))
840
+ pe[:, 0::2] = torch.sin(position * div_term)
841
+ pe[:, 1::2] = torch.cos(position * div_term)
842
+ pe = pe.unsqueeze(0)
843
+ self.pe = pe.to(device=x.device, dtype=x.dtype)
844
+
845
+ def forward(self, x: torch.Tensor):
846
+ """Add positional encoding.
847
+
848
+ Args:
849
+ x: torch.Tensor
850
+ Input tensor. shape is (batch, time, ...)
851
+
852
+ Returns:
853
+ torch.Tensor: Encoded tensor. Its shape is (batch, time, ...)
854
+
855
+ """
856
+ self.extend_pe(x)
857
+ x = x * self.xscale + self.pe[:, :x.size(1)]
858
+ return self.dropout(x)
859
+
860
+
861
+#### forward embedding layers starts here
862
+class MeanVarianceNormLayer(nn.Module):
863
+ """Mean/variance normalization layer.
864
+
865
+ Will subtract mean and multiply input by inverted standard deviation.
866
+ Typically used as a very first layer in a model.
867
+
868
+ Args:
869
+ input_size: int
870
+ layer input size.
871
+ """
872
+
873
+ def __init__(self, input_size):
874
+ super().__init__()
875
+ self.input_size = input_size
876
+ self.register_buffer("global_mean", torch.zeros(input_size))
877
+ self.register_buffer("global_invstd", torch.ones(input_size))
878
+ self.global_mean: Optional[Tensor]
879
+ self.global_invstd: Optional[Tensor]
880
+
881
+ def forward(self, input_: Tensor) -> Tensor:
882
+ """MeanVarianceNormLayer Forward
883
+
884
+ Args:
885
+ input_: torch.Tensor
886
+ input tensor.
887
+ """
888
+ return (input_ - self.global_mean) * self.global_invstd
889
+
890
+
891
+class CausalConv1D(nn.Conv1d):
892
+ """
893
+ A causal version of nn.Conv1d where each step would have limited access to
894
+ locations on its right or left
895
+ All arguments are the same as nn.Conv1d except padding.
896
+
897
+ If padding is set None, then paddings are set automatically to make it a
898
+ causal convolution where each location would not see any steps on its right.
899
+
900
+ If padding is set as a list (size of 2), then padding[0] would be used as
901
+ left padding and padding[1] as right padding.
902
+ It would make it possible to control the number of steps to be accessible
903
+ on the right and left.
904
+ This mode is not supported when stride > 1. padding[0]+padding[1] should
905
+ be equal to (kernel_size - 1).
906
+ """
907
+
908
+ def __init__(
909
+ self,
910
+ in_channels: int,
911
+ out_channels: int,
912
+ kernel_size: int,
913
+ stride: int = 1,
914
+ padding: Union[str, int] = 0,
915
+ dilation: int = 1,
916
+ groups: int = 1,
917
+ bias: bool = True,
918
+ padding_mode: str = "zeros",
919
+ device=None,
920
+ dtype=None,
921
+ ) -> None:
922
+ self.cache_drop_size = None
923
+ if padding is None:
924
+ self._left_padding = kernel_size - 1
925
+ self._right_padding = stride - 1
926
+ else:
927
+ if stride != 1 and padding != kernel_size - 1:
928
+ raise ValueError(
929
+ "No striding allowed for non-symmetric convolutions!")
930
+ if isinstance(padding, int):
931
+ self._left_padding = padding
932
+ self._right_padding = padding
933
+ elif (isinstance(padding, list) and len(padding) == 2
934
+ and padding[0] + padding[1] == kernel_size - 1):
935
+ self._left_padding = padding[0]
936
+ self._right_padding = padding[1]
937
+ else:
938
+ raise ValueError(f"Invalid padding param: {padding}!")
939
+
940
+ self._max_cache_len = self._left_padding
941
+
942
+ super().__init__(
943
+ in_channels=in_channels,
944
+ out_channels=out_channels,
945
+ kernel_size=kernel_size,
946
+ stride=stride,
947
+ padding=0,
948
+ dilation=dilation,
949
+ groups=groups,
950
+ bias=bias,
951
+ padding_mode=padding_mode,
952
+ device=device,
953
+ dtype=dtype,
954
+ )
955
+
956
+ def update_cache(self, x, cache=None):
957
+ if cache is None:
958
+ new_x = F.pad(x, pad=(self._left_padding, self._right_padding))
959
+ next_cache = cache
960
+ else:
961
+ new_x = F.pad(x, pad=(0, self._right_padding))
962
+ new_x = torch.cat([cache, new_x], dim=-1)
963
+ if self.cache_drop_size > 0:
964
+ next_cache = new_x[:, :, :-self.cache_drop_size]
965
+ else:
966
+ next_cache = new_x
967
+ next_cache = next_cache[:, :, -cache.size(-1):]
968
+ return new_x, next_cache
969
+
970
+ def forward(self, x, cache=None):
971
+ x, cache = self.update_cache(x, cache=cache)
972
+ x = super().forward(x)
973
+ if cache is None:
974
+ return x
975
+ else:
976
+ return x, cache
977
+
978
+
979
+class CausalConv2D(nn.Conv2d):
980
+ """
981
+ A causal version of nn.Conv2d where each location in the 2D matrix would
982
+ have no access to locations on its right or down
983
+ All arguments are the same as nn.Conv2d except padding which should be
984
+ set as None
985
+ """
986
+
987
+ def __init__(
988
+ self,
989
+ in_channels: int,
990
+ out_channels: int,
991
+ kernel_size: int,
992
+ stride: int = 1,
993
+ padding: Union[str, int] = 0,
994
+ dilation: int = 1,
995
+ groups: int = 1,
996
+ bias: bool = True,
997
+ padding_mode: str = "zeros",
998
+ device=None,
999
+ dtype=None,
1000
+ ) -> None:
1001
+ if padding is not None:
1002
+ raise ValueError(
1003
+ "Argument padding should be set to None for CausalConv2D.")
1004
+ self._left_padding = kernel_size - 1
1005
+ self._right_padding = stride - 1
1006
+
1007
+ padding = 0
1008
+ super().__init__(
1009
+ in_channels,
1010
+ out_channels,
1011
+ kernel_size,
1012
+ stride,
1013
+ padding,
1014
+ dilation,
1015
+ groups,
1016
+ bias,
1017
+ padding_mode,
1018
+ device,
1019
+ dtype,
1020
+ )
1021
+
1022
+ def forward(
1023
+ self,
1024
+ x,
1025
+ ):
1026
+ if self.training:
1027
+ x = F.pad(
1028
+ x,
1029
+ pad=(
1030
+ self._left_padding,
1031
+ self._right_padding,
1032
+ self._left_padding,
1033
+ self._right_padding,
1034
+ ),
1035
+ )
1036
+ else:
1037
+ x = F.pad(
1038
+ x,
1039
+ pad=(self._left_padding, self._right_padding, 0, 0),
1040
+ )
1041
+ x = super().forward(x)
1042
+ return x
1043
+
1044
+
1045
+class NemoConvSubsampling(torch.nn.Module):
1046
+ """Convlutional subsampling module, taken from NeMo ASR
1047
+ (https://github.com/NVIDIA/NeMo/blob/b367413645d5c72db3c2c96e46e95a
1048
+ 34501479cf/nemo/collections/asr/parts/submodules/subsampling.py)
1049
+
1050
+ Striding Subsampling: "Speech-Transformer: A No-Recurrence
1051
+ Sequence-to-Sequence Model for Speech Recognition" by Linhao Dong
1052
+ et al. (https://ieeexplore.ieee.org/document/8462506)
1053
+
1054
+
1055
+ Compared with the EncoderConv2D (`input_layer: custom`), this is a
1056
+ much simplified approach, and uses no LayerNorm and far fewer Conv2Ds.
1057
+ Moreover, depthwise convolutions are used to reduce FLOPs, but the first
1058
+ layer is kept as a regular convolution so as not to degrade accuracy.
1059
+
1060
+ `Striding` and `dw_striding` are the same except that the latter uses
1061
+ depthwise convolutions after the first layer, whereas the former does not.
1062
+
1063
+ Args:
1064
+ subsampling_factor (int): Time reduction factor
1065
+ feat_in (int): size of the input features
1066
+ feat_out (int): size of the output features
1067
+ subsampling (str): The subsampling technique, choose from
1068
+ {"striding", "dw-striding", "striding_conv1d",
1069
+ "dw_striding_conv1d"}
1070
+ conv_channels (int): Number of channels for the convolution layers,
1071
+ default is 256.
1072
+ subsampling_conv_chunking_factor (int): Input chunking factor which
1073
+ can be -1 (no chunking) 1 (auto) or a power of 2. Default is 1
1074
+ activation (Module): activation function, default is nn.ReLU()
1075
+ is_causal (bool): whether to use causal Conv1/2D, where each step will
1076
+ have limited access to locations on its right or left
1077
+ """
1078
+
1079
+ def __init__(
1080
+ self,
1081
+ feat_in,
1082
+ feat_out,
1083
+ subsampling_factor=4,
1084
+ subsampling="dw_striding",
1085
+ conv_channels=256,
1086
+ subsampling_conv_chunking_factor=1,
1087
+ activation=nn.ReLU(), # noqa: B008
1088
+ is_causal=False,
1089
+ ):
1090
+ super().__init__()
1091
+ self._subsampling = subsampling
1092
+ self._conv_channels = conv_channels
1093
+ self._feat_in = feat_in
1094
+ self._feat_out = feat_out
1095
+
1096
+ if subsampling_factor % 2 != 0:
1097
+ raise ValueError("Sampling factor should be a multiply of 2!")
1098
+ self._sampling_num = int(math.log(subsampling_factor, 2))
1099
+ self.subsampling_factor = subsampling_factor
1100
+ self.is_causal = is_causal
1101
+ self.subsampling_causal_cond = subsampling in (
1102
+ "dw_striding",
1103
+ "striding",
1104
+ "striding_conv1d",
1105
+ )
1106
+
1107
+ if (subsampling_conv_chunking_factor != -1
1108
+ and subsampling_conv_chunking_factor != 1
1109
+ and subsampling_conv_chunking_factor % 2 != 0):
1110
+ raise ValueError(
1111
+ "subsampling_conv_chunking_factor should be -1, 1, or a "\
1112
+ "power of 2"
1113
+ )
1114
+ self.subsampling_conv_chunking_factor = \
1115
+ subsampling_conv_chunking_factor
1116
+
1117
+ in_channels = 1
1118
+ layers = []
1119
+
1120
+ if subsampling == "dw_striding":
1121
+ self._stride = 2
1122
+ self._kernel_size = 3
1123
+ self._ceil_mode = False
1124
+
1125
+ if self.is_causal:
1126
+ self._left_padding = self._kernel_size - 1
1127
+ self._right_padding = self._stride - 1
1128
+ self._max_cache_len = subsampling_factor + 1
1129
+ else:
1130
+ self._left_padding = (self._kernel_size - 1) // 2
1131
+ self._right_padding = (self._kernel_size - 1) // 2
1132
+ self._max_cache_len = 0
1133
+
1134
+ # Layer 1
1135
+ if self.is_causal:
1136
+ layers.append(
1137
+ CausalConv2D(
1138
+ in_channels=in_channels,
1139
+ out_channels=conv_channels,
1140
+ kernel_size=self._kernel_size,
1141
+ stride=self._stride,
1142
+ padding=None,
1143
+ ))
1144
+ else:
1145
+ layers.append(
1146
+ torch.nn.Conv2d(
1147
+ in_channels=in_channels,
1148
+ out_channels=conv_channels,
1149
+ kernel_size=self._kernel_size,
1150
+ stride=self._stride,
1151
+ padding=self._left_padding,
1152
+ ))
1153
+ in_channels = conv_channels
1154
+ layers.append(activation)
1155
+
1156
+ for i in range(self._sampling_num - 1):
1157
+ if self.is_causal:
1158
+ layers.append(
1159
+ CausalConv2D(
1160
+ in_channels=in_channels,
1161
+ out_channels=in_channels,
1162
+ kernel_size=self._kernel_size,
1163
+ stride=self._stride,
1164
+ padding=None,
1165
+ groups=in_channels,
1166
+ ))
1167
+ else:
1168
+ layers.append(
1169
+ torch.nn.Conv2d(
1170
+ in_channels=in_channels,
1171
+ out_channels=in_channels,
1172
+ kernel_size=self._kernel_size,
1173
+ stride=self._stride,
1174
+ padding=self._left_padding,
1175
+ groups=in_channels,
1176
+ ))
1177
+
1178
+ layers.append(
1179
+ torch.nn.Conv2d(
1180
+ in_channels=in_channels,
1181
+ out_channels=conv_channels,
1182
+ kernel_size=1,
1183
+ stride=1,
1184
+ padding=0,
1185
+ groups=1,
1186
+ ))
1187
+ layers.append(activation)
1188
+ in_channels = conv_channels
1189
+
1190
+ elif subsampling == "striding":
1191
+ self._stride = 2
1192
+ self._kernel_size = 3
1193
+ self._ceil_mode = False
1194
+
1195
+ if self.is_causal:
1196
+ self._left_padding = self._kernel_size - 1
1197
+ self._right_padding = self._stride - 1
1198
+ self._max_cache_len = subsampling_factor + 1
1199
+ else:
1200
+ self._left_padding = (self._kernel_size - 1) // 2
1201
+ self._right_padding = (self._kernel_size - 1) // 2
1202
+ self._max_cache_len = 0
1203
+
1204
+ for i in range(self._sampling_num):
1205
+ if self.is_causal:
1206
+ layers.append(
1207
+ CausalConv2D(
1208
+ in_channels=in_channels,
1209
+ out_channels=conv_channels,
1210
+ kernel_size=self._kernel_size,
1211
+ stride=self._stride,
1212
+ padding=None,
1213
+ ))
1214
+ else:
1215
+ layers.append(
1216
+ torch.nn.Conv2d(
1217
+ in_channels=in_channels,
1218
+ out_channels=conv_channels,
1219
+ kernel_size=self._kernel_size,
1220
+ stride=self._stride,
1221
+ padding=self._left_padding,
1222
+ ))
1223
+ layers.append(activation)
1224
+ in_channels = conv_channels
1225
+
1226
+ elif subsampling == "striding_conv1d":
1227
+ in_channels = feat_in
1228
+
1229
+ self._stride = 2
1230
+ self._kernel_size = 5
1231
+ self._ceil_mode = False
1232
+
1233
+ if self.is_causal:
1234
+ self._left_padding = self._kernel_size - 1
1235
+ self._right_padding = self._stride - 1
1236
+ self._max_cache_len = subsampling_factor + 1
1237
+ else:
1238
+ self._left_padding = (self._kernel_size - 1) // 2
1239
+ self._right_padding = (self._kernel_size - 1) // 2
1240
+ self._max_cache_len = 0
1241
+
1242
+ for i in range(self._sampling_num):
1243
+ if self.is_causal:
1244
+ layers.append(
1245
+ CausalConv1D(
1246
+ in_channels=in_channels,
1247
+ out_channels=(feat_out if self._sampling_num == i +
1248
+ 1 else conv_channels),
1249
+ kernel_size=self._kernel_size,
1250
+ stride=self._stride,
1251
+ padding=None,
1252
+ ))
1253
+ else:
1254
+ layers.append(
1255
+ torch.nn.Conv1d(
1256
+ in_channels=in_channels,
1257
+ out_channels=(feat_out if self._sampling_num == i +
1258
+ 1 else conv_channels),
1259
+ kernel_size=self._kernel_size,
1260
+ stride=self._stride,
1261
+ padding=self._left_padding,
1262
+ ))
1263
+ layers.append(activation)
1264
+ in_channels = conv_channels
1265
+
1266
+ elif subsampling == "dw_striding_conv1d":
1267
+ in_channels = feat_in
1268
+
1269
+ self._stride = 2
1270
+ self._kernel_size = 5
1271
+ self._ceil_mode = False
1272
+
1273
+ self._left_padding = (self._kernel_size - 1) // 2
1274
+ self._right_padding = (self._kernel_size - 1) // 2
1275
+
1276
+ # Layer 1
1277
+ layers.extend([
1278
+ torch.nn.Conv1d(
1279
+ in_channels=in_channels,
1280
+ out_channels=in_channels,
1281
+ kernel_size=self._kernel_size,
1282
+ stride=self._stride,
1283
+ padding=self._left_padding,
1284
+ groups=in_channels,
1285
+ ),
1286
+ torch.nn.Conv1d(
1287
+ in_channels=in_channels,
1288
+ out_channels=(feat_out if self._sampling_num == 1 else
1289
+ conv_channels),
1290
+ kernel_size=1,
1291
+ stride=1,
1292
+ padding=0,
1293
+ groups=1,
1294
+ ),
1295
+ ])
1296
+ in_channels = conv_channels
1297
+ layers.append(activation)
1298
+
1299
+ for i in range(self._sampling_num - 1):
1300
+ layers.extend([
1301
+ torch.nn.Conv1d(
1302
+ in_channels=in_channels,
1303
+ out_channels=in_channels,
1304
+ kernel_size=self._kernel_size,
1305
+ stride=self._stride,
1306
+ padding=self._left_padding,
1307
+ groups=in_channels,
1308
+ ),
1309
+ torch.nn.Conv1d(
1310
+ in_channels=in_channels,
1311
+ out_channels=(feat_out if self._sampling_num == i +
1312
+ 2 else conv_channels),
1313
+ kernel_size=1,
1314
+ stride=1,
1315
+ padding=0,
1316
+ groups=1,
1317
+ ),
1318
+ ])
1319
+ layers.append(activation)
1320
+ in_channels = conv_channels
1321
+
1322
+ else:
1323
+ raise ValueError(f"Not valid sub-sampling: {subsampling}!")
1324
+
1325
+ if subsampling in ["dw_striding", "striding"]:
1326
+ in_length = torch.tensor(feat_in, dtype=torch.float)
1327
+ out_length = calc_length(
1328
+ lengths=in_length,
1329
+ all_paddings=self._left_padding + self._right_padding,
1330
+ kernel_size=self._kernel_size,
1331
+ stride=self._stride,
1332
+ ceil_mode=self._ceil_mode,
1333
+ repeat_num=self._sampling_num,
1334
+ )
1335
+ self.out = torch.nn.Linear(conv_channels * int(out_length),
1336
+ feat_out)
1337
+ self.conv2d_subsampling = True
1338
+ elif subsampling in ["striding_conv1d", "dw_striding_conv1d"]:
1339
+ self.out = None
1340
+ self.conv2d_subsampling = False
1341
+ else:
1342
+ raise ValueError(f"Not valid sub-sampling: {subsampling}!")
1343
+
1344
+ self.conv = torch.nn.Sequential(*layers)
1345
+
1346
+ def get_sampling_frames(self):
1347
+ return [1, self.subsampling_factor]
1348
+
1349
+ def get_streaming_cache_size(self):
1350
+ return [0, self.subsampling_factor + 1]
1351
+
1352
+ def forward(self, x, mask):
1353
+ """
1354
+ Forward method for NeMo subsampling.
1355
+
1356
+ Args:
1357
+ x[Batch, Time, Filters]: torch.Tensor
1358
+ input tensor
1359
+ x_mask: torch.Tensor
1360
+ input mask
1361
+
1362
+ Returns:
1363
+ x: torch.Tensor
1364
+ Resulting tensor from subsampling (B, T //
1365
+ time_reduction_factor, feat_out)
1366
+ pad_mask: torch.Tensor
1367
+ tensor of padded hidden state sequences (B, 1, T //
1368
+ time_reduction_factor)
1369
+ """
1370
+ x = x.unsqueeze(1) if self.conv2d_subsampling else x.transpose(1, 2)
1371
+
1372
+ # split inputs if chunking_factor is set
1373
+ if (self.subsampling_conv_chunking_factor != -1
1374
+ and self.conv2d_subsampling):
1375
+ if self.subsampling_conv_chunking_factor == 1:
1376
+ # if subsampling_conv_chunking_factor is 1, we split only
1377
+ # if needed.
1378
+ # avoiding a bug / feature limiting indexing of tensors
1379
+ # to 2**31.
1380
+ # see https://github.com/pytorch/pytorch/issues/80020
1381
+ x_ceil = (2**31 / self._conv_channels * self._stride *
1382
+ self._stride)
1383
+ need_to_split = torch.numel(x) > x_ceil
1384
+ else:
1385
+ # if subsampling_conv_chunking_factor > 1 we always split
1386
+ need_to_split = True
1387
+
1388
+ if need_to_split:
1389
+ x, success = self.conv_split_by_batch(x)
1390
+ if not success: # if unable to split by batch, try by channel
1391
+ if self._subsampling == "dw_striding":
1392
+ x = self.conv_split_by_channel(x)
1393
+ else:
1394
+ x = self.conv(x) # try anyway
1395
+ else:
1396
+ x = self.conv(x)
1397
+ else:
1398
+ x = self.conv(x)
1399
+
1400
+ # Flatten Channel and Frequency Axes
1401
+ if self.conv2d_subsampling:
1402
+ b, c, t, f = x.size()
1403
+ x = self.out(x.transpose(1, 2).reshape(b, t, -1))
1404
+ # Transpose to Channel Last mode
1405
+ else:
1406
+ x = x.transpose(1, 2)
1407
+
1408
+ if mask is None:
1409
+ return x, None
1410
+
1411
+ max_audio_length = x.shape[1]
1412
+ feature_lens = mask.sum(1)
1413
+ padding_length = torch.ceil(feature_lens / self.subsampling_factor)
1414
+ if self.is_causal and self.subsampling_causal_cond:
1415
+ feature_lens_remainder = feature_lens % self.subsampling_factor
1416
+ padding_length[feature_lens_remainder != 1] += 1
1417
+ pad_mask = torch.arange(0, max_audio_length, device=x.device).expand(
1418
+ padding_length.size(0), -1) < padding_length.unsqueeze(1)
1419
+ return x, pad_mask.unsqueeze(1)
1420
+
1421
+ def reset_parameters(self):
1422
+ # initialize weights
1423
+ if self._subsampling == "dw_striding":
1424
+ with torch.no_grad():
1425
+ # init conv
1426
+ scale = 1.0 / self._kernel_size
1427
+ dw_max = (self._kernel_size**2)**-0.5
1428
+ pw_max = self._conv_channels**-0.5
1429
+
1430
+ torch.nn.init.uniform_(self.conv[0].weight, -scale, scale)
1431
+ torch.nn.init.uniform_(self.conv[0].bias, -scale, scale)
1432
+
1433
+ for idx in range(2, len(self.conv), 3):
1434
+ torch.nn.init.uniform_(self.conv[idx].weight, -dw_max,
1435
+ dw_max)
1436
+ torch.nn.init.uniform_(self.conv[idx].bias, -dw_max,
1437
+ dw_max)
1438
+ torch.nn.init.uniform_(self.conv[idx + 1].weight, -pw_max,
1439
+ pw_max)
1440
+ torch.nn.init.uniform_(self.conv[idx + 1].bias, -pw_max,
1441
+ pw_max)
1442
+
1443
+ # init fc (80 * 64 = 5120 from https://github.com/kssteven418/
1444
+ # Squeezeformer/blob/13c97d6cf92f2844d2cb3142b4c5bfa9ad1a8951/
1445
+ # src/models/conformer_encoder.py#L487
1446
+ fc_scale = (self._feat_out * self._feat_in /
1447
+ self._sampling_num)**-0.5
1448
+ torch.nn.init.uniform_(self.out.weight, -fc_scale, fc_scale)
1449
+ torch.nn.init.uniform_(self.out.bias, -fc_scale, fc_scale)
1450
+
1451
+ def conv_split_by_batch(self, x):
1452
+ """Tries to split input by batch, run conv and concat results"""
1453
+ b, _, _, _ = x.size()
1454
+ if b == 1: # can't split if batch size is 1
1455
+ return x, False
1456
+
1457
+ if self.subsampling_conv_chunking_factor > 1:
1458
+ cf = self.subsampling_conv_chunking_factor
1459
+ else:
1460
+ # avoiding a bug / feature limiting indexing of tensors to 2**31
1461
+ # see https://github.com/pytorch/pytorch/issues/80020
1462
+ x_ceil = 2**31 / self._conv_channels * self._stride * self._stride
1463
+ p = math.ceil(math.log(torch.numel(x) / x_ceil, 2))
1464
+ cf = 2**p
1465
+
1466
+ new_batch_size = b // cf
1467
+ if new_batch_size == 0: # input is too big
1468
+ return x, False
1469
+
1470
+ return (
1471
+ torch.cat([
1472
+ self.conv(chunk)
1473
+ for chunk in torch.split(x, new_batch_size, 0)
1474
+ ]),
1475
+ True,
1476
+ )
1477
+
1478
+ def conv_split_by_channel(self, x):
1479
+ """For dw convs, tries to split input by time, run conv and concat
1480
+ results"""
1481
+ x = self.conv[0](x) # full conv2D
1482
+ x = self.conv[1](x) # activation
1483
+
1484
+ for i in range(self._sampling_num - 1):
1485
+ _, c, t, _ = x.size()
1486
+
1487
+ if self.subsampling_conv_chunking_factor > 1:
1488
+ cf = self.subsampling_conv_chunking_factor
1489
+ else:
1490
+ # avoiding a bug / feature limiting indexing of tensors
1491
+ # to 2**31
1492
+ # see https://github.com/pytorch/pytorch/issues/80020
1493
+ p = math.ceil(math.log(torch.numel(x) / 2**31, 2))
1494
+ cf = 2**p
1495
+
1496
+ new_c = int(c // cf)
1497
+ if new_c == 0:
1498
+ new_c = 1
1499
+
1500
+ new_t = int(t // cf)
1501
+ if new_t == 0:
1502
+ new_t = 1
1503
+
1504
+ x = self.channel_chunked_conv(self.conv[i * 3 + 2], new_c,
1505
+ x) # conv2D, depthwise
1506
+
1507
+ # splitting pointwise convs by time
1508
+ x = torch.cat(
1509
+ [
1510
+ self.conv[i * 3 + 3](chunk)
1511
+ for chunk in torch.split(x, new_t, 2)
1512
+ ],
1513
+ 2,
1514
+ ) # conv2D, pointwise
1515
+ x = self.conv[i * 3 + 4](x) # activation
1516
+ return x
1517
+
1518
+ def channel_chunked_conv(self, conv, chunk_size, x):
1519
+ """Performs channel chunked convolution"""
1520
+
1521
+ ind = 0
1522
+ out_chunks = []
1523
+ for chunk in torch.split(x, chunk_size, 1):
1524
+ step = chunk.size()[1]
1525
+
1526
+ if self.is_causal:
1527
+ chunk = nn.functional.pad(
1528
+ chunk,
1529
+ pad=(
1530
+ self._kernel_size - 1,
1531
+ self._stride - 1,
1532
+ self._kernel_size - 1,
1533
+ self._stride - 1,
1534
+ ),
1535
+ )
1536
+ ch_out = nn.functional.conv2d(
1537
+ chunk,
1538
+ conv.weight[ind:ind + step, :, :, :],
1539
+ bias=conv.bias[ind:ind + step],
1540
+ stride=self._stride,
1541
+ padding=0,
1542
+ groups=step,
1543
+ )
1544
+ else:
1545
+ ch_out = nn.functional.conv2d(
1546
+ chunk,
1547
+ conv.weight[ind:ind + step, :, :, :],
1548
+ bias=conv.bias[ind:ind + step],
1549
+ stride=self._stride,
1550
+ padding=self._left_padding,
1551
+ groups=step,
1552
+ )
1553
+ out_chunks.append(ch_out)
1554
+ ind += step
1555
+
1556
+ return torch.cat(out_chunks, 1)
1557
+
1558
+ def change_subsampling_conv_chunking_factor(
1559
+ self, subsampling_conv_chunking_factor: int):
1560
+ if (subsampling_conv_chunking_factor != -1
1561
+ and subsampling_conv_chunking_factor != 1
1562
+ and subsampling_conv_chunking_factor % 2 != 0):
1563
+ raise ValueError(
1564
+ "subsampling_conv_chunking_factor should be -1, 1, or a "\
1565
+ "power of 2"
1566
+ )
1567
+ self.subsampling_conv_chunking_factor = subsampling_conv_chunking_factor
1568
+
1569
+
1570
+def calc_length(lengths,
1571
+ all_paddings,
1572
+ kernel_size,
1573
+ stride,
1574
+ ceil_mode,
1575
+ repeat_num=1):
1576
+ """Calculates the output length of a Tensor passed through a convolution or
1577
+ max pooling layer"""
1578
+ add_pad: float = all_paddings - kernel_size
1579
+ one: float = 1.0
1580
+ for i in range(repeat_num):
1581
+ lengths = (torch.div(lengths.to(dtype=torch.float) + add_pad, stride) +
1582
+ one)
1583
+ lengths = torch.ceil(lengths) if ceil_mode else torch.floor(lengths)
1584
+ return lengths.to(dtype=torch.int)
1585
+
1586
+
1587
+#### multihead attention starts here
1588
+class AttModule(nn.Module):
1589
+ """Attention abstraction module"""
1590
+
1591
+ def __init__(self):
1592
+ super().__init__()
1593
+ self.export_mode = False
1594
+
1595
+ def set_export(self, mode=True):
1596
+ """set the export mode"""
1597
+ self.export_mode = mode
1598
+
1599
+ def forward(
1600
+ self,
1601
+ x: Tensor,
1602
+ memory: Optional[Tensor] = None,
1603
+ pos_emb: Optional[Tensor] = None,
1604
+ att_mask: Optional[Tensor] = None,
1605
+ ) -> Tuple[Tensor, Tensor, Optional[Tensor], Optional[Tensor]]:
1606
+ """AttModule forward
1607
+
1608
+ Args:
1609
+ x: torch.Tensor
1610
+ input tensor.
1611
+ memory: torch.Tensor, optional
1612
+ memory tensor.
1613
+ pos_emb: torch.Tensor, optional
1614
+ positional encoder embedding.
1615
+ att_mask: torch.Tensor, optional
1616
+ attention mask tensor.
1617
+ """
1618
+ return x, memory, pos_emb, att_mask
1619
+
1620
+
1621
+class AttBlock(Block, AttModule):
1622
+ """Attention Block module to support both Attention and Block module."""
1623
+
1624
+ def memory_dims(self, max_len=False):
1625
+ """memory dimensions"""
1626
+ return (1, self.input_size)
1627
+
1628
+
1629
+def masked_softmax(
1630
+ scores,
1631
+ mask: Optional[Tensor],
1632
+):
1633
+ if mask is not None:
1634
+ mask = mask.unsqueeze(1).eq(0) # (batch, 1, time1, time2)
1635
+ scores = scores.masked_fill(mask, -torch.inf)
1636
+ attn = torch.softmax(scores, dim=-1).masked_fill(
1637
+ mask, 0.0) # (batch, head, time1, time2)
1638
+ else:
1639
+ attn = torch.softmax(scores, dim=-1) # (batch, head, time1, time2)
1640
+ return attn
1641
+
1642
+
1643
+class MultiHeadedAttention(nn.Module):
1644
+ """Multi-Head Attention layer with optional relative position embedding
1645
+ and GLU.
1646
+
1647
+ Args:
1648
+ n_head: int
1649
+ the number of heads.
1650
+ n_feat: int
1651
+ input size features.
1652
+ dropout_rate: float
1653
+ dropout rate.
1654
+ use_LN: bool
1655
+ apply layer norm or not
1656
+ dropout_at_output: bool
1657
+ whether to apply dropout at output
1658
+ attention_inner_dim: int, optional
1659
+ the attention dimension used in the class,
1660
+ it can be different from the input dimension n_feat.
1661
+ default: -1 (equal to n_feat).
1662
+ use_pt_scaled_dot_product_attention: bool, optional
1663
+ if set True, use pytorch scaled dot product attention in training.
1664
+ NOTE: this will NOT be used in ONNX decoding due to a lack of
1665
+ support. In that case, we use the original attention
1666
+ implementation, which shows no regression.
1667
+ default: False.
1668
+ n_value: int, optional
1669
+ if set to values other than -1, use a different dimension for
1670
+ value. With the default value (i.e. -1), it is backward compatible.
1671
+ group_size: int, optional. must divide `n_head`
1672
+ if group_size > 1: GQA
1673
+ if group_size = 1: MHA
1674
+ if group_size = n_head: MQA
1675
+ """
1676
+
1677
+ inv_sqrt_d_k: torch.jit.Final[float]
1678
+ h: torch.jit.Final[int]
1679
+ h_k: torch.jit.Final[int]
1680
+ g: torch.jit.Final[int]
1681
+
1682
+ def __init__(
1683
+ self,
1684
+ n_head,
1685
+ n_feat,
1686
+ dropout_rate,
1687
+ attention_inner_dim=-1,
1688
+ glu_type="swish",
1689
+ bias_in_glu=True,
1690
+ use_pt_scaled_dot_product_attention=False,
1691
+ n_value=-1,
1692
+ group_size: int = 1,
1693
+ ):
1694
+ super().__init__()
1695
+ if n_value == -1:
1696
+ n_value = n_feat
1697
+ if attention_inner_dim == -1:
1698
+ attention_inner_dim = n_feat
1699
+ assert attention_inner_dim % n_head == 0
1700
+
1701
+ # We assume d_v always equals d_k
1702
+ self.d_k = attention_inner_dim // n_head
1703
+ self.inv_sqrt_d_k = 1.0 / math.sqrt(self.d_k)
1704
+ self.h = n_head
1705
+ assert n_head % group_size == 0, "group_size must divide n_head"
1706
+ self.g = group_size
1707
+ self.h_k = n_head // group_size
1708
+
1709
+ self.linear_q = nn.Linear(n_feat, attention_inner_dim)
1710
+ self.linear_k = nn.Linear(n_feat, attention_inner_dim // group_size)
1711
+ self.linear_v = nn.Linear(n_value, attention_inner_dim // group_size)
1712
+ self.linear_out = nn.Linear(attention_inner_dim // group_size, n_value)
1713
+
1714
+ self.attn = torch.jit.Attribute(None, Optional[Tensor])
1715
+ self.dropout = nn.Dropout(p=dropout_rate)
1716
+ self.dropout_rate = dropout_rate
1717
+ self.use_pt_scaled_dot_product_attention = (
1718
+ use_pt_scaled_dot_product_attention)
1719
+
1720
+ if use_pt_scaled_dot_product_attention and group_size > 1:
1721
+ raise ValueError("Cannot use PT Scaled Attention with GQA")
1722
+
1723
+ # Torchscript eager quantization. Note that these functions below are
1724
+ # NOOPs and have very little impact on performance unless quantization
1725
+ # is enabled.
1726
+ self.quant_q = torch.ao.quantization.QuantStub()
1727
+ self.quant_x = torch.ao.quantization.QuantStub()
1728
+ self.dequant = torch.ao.quantization.DeQuantStub()
1729
+ self.ffunc = torch.ao.nn.quantized.FloatFunctional()
1730
+
1731
+ def forward(
1732
+ self,
1733
+ query: Tensor,
1734
+ key: Tensor,
1735
+ value: Tensor,
1736
+ pos_k: Tensor,
1737
+ pos_v: Tensor,
1738
+ mask: Optional[Tensor],
1739
+ relative_attention_bias: Optional[Tensor] = None,
1740
+ ):
1741
+ """Compute 'Scaled Dot Product Attention'.
1742
+
1743
+ Args:
1744
+ query: torch.Tensor
1745
+ query tensor (batch, time1, size)
1746
+ key: torch.Tensor
1747
+ key tensor (batch, time2, size)
1748
+ value: torch.Tensor
1749
+ value tensor (batch, time1, size)
1750
+ pos_k: torch.Tensor
1751
+ key tensor used for relative positional embedding.
1752
+ pos_v: torch.Tensor
1753
+ value tensor used for relative positional embedding.
1754
+ mask: torch.Tensor
1755
+ mask tensor (batch, time1, time2)
1756
+ relative_attention_bias: torch.Tensor
1757
+ bias added to attention logits w.r.t. relative positions
1758
+ (1, n_head, time1, time2)
1759
+ """
1760
+ n_batch = query.size(0)
1761
+
1762
+ q = self.linear_q(query).view(n_batch, -1, self.h,
1763
+ self.d_k) # (b, t, d)
1764
+ k = self.linear_k(key).view(n_batch, -1, self.h_k,
1765
+ self.d_k) # (b, t, d)
1766
+ v = self.linear_v(value).view(n_batch, -1, self.h_k, self.d_k)
1767
+ q = (q.transpose(1, 2) if self.use_pt_scaled_dot_product_attention
1768
+ and not torch.jit.is_scripting() else q.transpose(1, 2) *
1769
+ self.inv_sqrt_d_k)
1770
+ k = k.transpose(1, 2) # (batch, head_k, time2, d_k)
1771
+ v = v.transpose(1, 2) # (batch, head_k, time2, d_k)
1772
+
1773
+ if (self.use_pt_scaled_dot_product_attention
1774
+ and not torch.jit.is_scripting()):
1775
+ attn_mask = None
1776
+ if mask is not None:
1777
+ mask = mask.unsqueeze(1)
1778
+ if relative_attention_bias is not None:
1779
+ attn_mask = mask + relative_attention_bias
1780
+ else:
1781
+ attn_mask = mask
1782
+ if mask.dtype != q.dtype:
1783
+ attn_mask = attn_mask.to(q.dtype)
1784
+
1785
+ with torch.backends.cuda.sdp_kernel(enable_flash=True,
1786
+ enable_math=True,
1787
+ enable_mem_efficient=True):
1788
+ x = torch.nn.functional.scaled_dot_product_attention(
1789
+ q,
1790
+ k,
1791
+ v,
1792
+ attn_mask=attn_mask,
1793
+ dropout_p=self.dropout_rate,
1794
+ )
1795
+ else:
1796
+ if self.h != self.h_k:
1797
+ q = q.reshape(n_batch, self.g, self.h_k, -1, self.d_k)
1798
+ A = torch.einsum("b g h t d, b h s d -> b h t s", q, k)
1799
+ else:
1800
+ A = torch.matmul(q, k.transpose(-2, -1))
1801
+ if pos_k is not None:
1802
+ if self.h != self.h_k:
1803
+ B = torch.einsum("b g h t d, t s d -> b h t s", q, pos_k)
1804
+ else:
1805
+ reshape_q = (q.contiguous().view(n_batch * self.h, -1,
1806
+ self.d_k).transpose(0, 1)
1807
+ ) # (t1,nh,dk)
1808
+ B = torch.matmul(reshape_q,
1809
+ pos_k.transpose(-2,
1810
+ -1)) # pos_k: (t1,dk,t2)
1811
+ B = B.transpose(0, 1).view(n_batch, self.h, pos_k.size(0),
1812
+ pos_k.size(1))
1813
+ scores = A + B
1814
+ else:
1815
+ scores = A
1816
+
1817
+ if relative_attention_bias is not None:
1818
+ scores = scores + relative_attention_bias
1819
+
1820
+ attn = masked_softmax(scores, mask) # (batch, head, time1, time2)
1821
+
1822
+ self.attn = attn
1823
+
1824
+ p_attn = self.dropout(attn)
1825
+ x = torch.matmul(p_attn.to(v.dtype),
1826
+ v) # (batch, head, time1, d_k)
1827
+ if pos_v is not None:
1828
+ reshape_attn = (p_attn.contiguous().view(
1829
+ n_batch * self.h, pos_v.size(0),
1830
+ pos_v.size(1)).transpose(0, 1)) # (t1, bh, t2)
1831
+
1832
+ attn_v = (torch.matmul(reshape_attn, pos_v).transpose(
1833
+ 0, 1).contiguous().view(n_batch, self.h, pos_v.size(0),
1834
+ self.d_k))
1835
+ x = x + attn_v
1836
+ x = (x.transpose(1, 2).contiguous().view(n_batch, -1,
1837
+ self.h_k * self.d_k)
1838
+ ) # (batch, time1, d_model)
1839
+
1840
+ return self.linear_out(x) # (batch, time1, d_model)
1841
+
1842
+
1843
+def validate_checkpointing_config(activation_checkpointing):
1844
+ """validate activation checkpointing configuration"""
1845
+ if isinstance(activation_checkpointing, str):
1846
+ assert activation_checkpointing in (
1847
+ "",
1848
+ "checkpoint",
1849
+ "offload",
1850
+ ), "activation_checkpointing has to be a dict or a str in "\
1851
+ "('', 'checkpoint', 'offload')."
1852
+ elif isinstance(activation_checkpointing, dict):
1853
+ assert activation_checkpointing.get("module", "transformer") in (
1854
+ "transformer",
1855
+ "attention",
1856
+ ), "module in activation_checkpointing has to be in "\
1857
+ "('transformer', 'attention')."
1858
+ else:
1859
+ raise ValueError("activation_checkpointing has to be a str"\
1860
+ " or dict.")
1861
+
1862
+
1863
+def embedding_checkpoint_wrapper(
1864
+ activation_checkpointing: Union[str, Dict], ) -> Callable:
1865
+ """return encoder embedding activation checkpoint wrapper"""
1866
+ validate_checkpointing_config(activation_checkpointing)
1867
+
1868
+ if isinstance(activation_checkpointing, str):
1869
+ if activation_checkpointing:
1870
+ if activation_checkpointing == "offload":
1871
+ return offload_wrapper
1872
+ return partial(checkpoint_wrapper)
1873
+ return lambda x: x
1874
+
1875
+ if isinstance(activation_checkpointing, dict):
1876
+ enabled = activation_checkpointing.get("embed", False)
1877
+ if enabled:
1878
+ offloading = activation_checkpointing.get("offload", False)
1879
+ if offloading:
1880
+ return offload_wrapper
1881
+ impl = (CheckpointImpl.REENTRANT if activation_checkpointing.get(
1882
+ "reentrant", False) else CheckpointImpl.NO_REENTRANT)
1883
+ return partial(checkpoint_wrapper, checkpoint_impl=impl)
1884
+ return lambda x: x
1885
+ raise ValueError("Invalid activation_checkpointing config")
1886
+
1887
+
1888
+def attn_checkpointing(activation_checkpointing: Union[str, Dict],
1889
+ i) -> Union[str, Dict]:
1890
+ """return activation checkpointing config for attention layer"""
1891
+ if isinstance(activation_checkpointing, str):
1892
+ return ""
1893
+
1894
+ if isinstance(activation_checkpointing, dict):
1895
+ target_layer_cls = activation_checkpointing.get(
1896
+ "module", "transformer")
1897
+ checkpointing_interval = activation_checkpointing.get("interval", 1)
1898
+ if target_layer_cls == "attention" and i % checkpointing_interval == 0:
1899
+ return activation_checkpointing
1900
+ return ""
1901
+
1902
+ raise ValueError("Invalid activation_checkpointing config")
1903
+
1904
+
1905
+class MultiSequential(torch.nn.Sequential):
1906
+ """Multi-input multi-output torch.nn.Sequential"""
1907
+
1908
+ @torch.jit.ignore
1909
+ def forward(self, *args):
1910
+ """Forward method implementation."""
1911
+ for m in self:
1912
+ args = m(*args)
1913
+ return args
1914
+
1915
+
1916
+def repeat(repeat_num, module_gen_fn):
1917
+ """repeat module N times
1918
+
1919
+ :param int repeat_num: repeat time
1920
+ :param function module_gen_fn: function to generate module
1921
+ :return: repeated modules
1922
+ :rtype: MultiSequential
1923
+ """
1924
+ return MultiSequential(*[module_gen_fn(i) for i in range(repeat_num)])
1925
+
1926
+
1927
+def get_offset(input_layer: str, time_reduction: int):
1928
+ """Get an offset. We will use the offset for determining #frames of a
1929
+ subsampled feature.
1930
+
1931
+ Args:
1932
+ input_layer (str): Type of an input layer
1933
+ time_reduction (int): time reduction factor for downsampling a feature
1934
+ Returns:
1935
+ int: offset
1936
+ """
1937
+ if input_layer in ("conv2d", "nemo_conv") and time_reduction == 4:
1938
+ return 3
1939
+ if input_layer in ("conv2d", ) and time_reduction == 6:
1940
+ return 1
1941
+ if input_layer in ("conv2d", "nemo_conv") and time_reduction == 8:
1942
+ return 7
1943
+ return 0
1944
+
1945
+
1946
+def unfold_tensor(xs_pad, max_seq_len):
1947
+ """
1948
+ For a given tensor with shape of (N, T, D), if sequence length T is
1949
+ longer than max_seq_len, this function unfold it to a
1950
+ (NT', max_seq_len, D) where T' is T // max_seq_len.
1951
+ Args:
1952
+ xs_pad: N, T, D
1953
+ """
1954
+ _, _, D = xs_pad.shape
1955
+ xs_pad = xs_pad.transpose(-1, -2) # convert to N, D, T
1956
+ # N x D x 1 x T => N x (D x max_seq_len) x T'
1957
+ xs_pad = F.unfold(
1958
+ xs_pad[..., None, :],
1959
+ kernel_size=(1, max_seq_len),
1960
+ stride=(1, max_seq_len),
1961
+ )
1962
+ new_bsz, _, slen = xs_pad.shape
1963
+ # N x D x max_seq_len x T'
1964
+ xs_pad = xs_pad.view(new_bsz, -1, max_seq_len, slen)
1965
+ # N x T' x max_seq_len x D
1966
+ xs_pad = xs_pad.permute(0, 3, 2, 1).contiguous()
1967
+ # NT' x max_seq_len x D
1968
+ xs_pad = xs_pad.view(-1, max_seq_len, D)
1969
+ return xs_pad
vllm/model_executor/models/registry.py
+1
@@ -182,6 +182,7 @@ _MULTIMODAL_MODELS = {
182
"Qwen2_5_VLForConditionalGeneration": ("qwen2_5_vl", "Qwen2_5_VLForConditionalGeneration"), # noqa: E501
183
"Qwen2AudioForConditionalGeneration": ("qwen2_audio", "Qwen2AudioForConditionalGeneration"), # noqa: E501
184
"UltravoxModel": ("ultravox", "UltravoxModel"),
185
+ "Phi4MMForCausalLM": ("phi4mm", "Phi4MMForCausalLM"),
186
# [Encoder-decoder]
187
"Florence2ForConditionalGeneration": ("florence2", "Florence2ForConditionalGeneration"), # noqa: E501
188
"MllamaForConditionalGeneration": ("mllama", "MllamaForConditionalGeneration"), # noqa: E501
vllm/model_executor/models/vision_siglip_navit.py
new
+1966
@@ -0,0 +1,1966 @@
1
+# SPDX-License-Identifier: Apache-2.0
2
+# Copyright 2024 The HuggingFace Inc. team. All rights reserved.
3
+#
4
+# Licensed under the Apache License, Version 2.0 (the "License");
5
+# you may not use this file except in compliance with the License.
6
+# You may obtain a copy of the License at
7
+#
8
+# http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+# Unless required by applicable law or agreed to in writing, software
11
+# distributed under the License is distributed on an "AS IS" BASIS,
12
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+# See the License for the specific language governing permissions and
14
+# limitations under the License.
15
+""" Siglip model configuration"""
16
+
17
+import math
18
+import os
19
+import warnings
20
+from dataclasses import dataclass
21
+from typing import Any, Optional, Tuple, Union
22
+
23
+import numpy as np
24
+import torch
25
+import torch.nn.functional as F
26
+import torch.utils.checkpoint
27
+from torch import nn
28
+from torch.nn.init import _calculate_fan_in_and_fan_out
29
+from transformers.activations import ACT2FN
30
+from transformers.configuration_utils import PretrainedConfig
31
+from transformers.modeling_attn_mask_utils import _prepare_4d_attention_mask
32
+from transformers.modeling_outputs import (BaseModelOutput,
33
+ BaseModelOutputWithPooling)
34
+from transformers.modeling_utils import PreTrainedModel
35
+from transformers.utils import (ModelOutput, add_start_docstrings,
36
+ add_start_docstrings_to_model_forward, logging,
37
+ replace_return_docstrings)
38
+
39
+from vllm.platforms import _Backend
40
+
41
+from .vision import get_vit_attn_backend
42
+
43
+logger = logging.get_logger(__name__)
44
+
45
+SIGLIP_PRETRAINED_CONFIG_ARCHIVE_MAP = {
46
+ "google/siglip-base-patch16-224":
47
+ "https://huggingface.co/google/siglip-base-patch16-224/"\
48
+ "resolve/main/config.json",
49
+}
50
+
51
+
52
+class SiglipTextConfig(PretrainedConfig):
53
+ r"""
54
+ This is the configuration class to store the configuration of a
55
+ [`SiglipTextModel`]. It is used to instantiate a Siglip text encoder
56
+ according to the specified arguments, defining the model architecture.
57
+ Instantiating a configuration with the defaults will yield a similar
58
+ configuration to that of the text encoder of the Siglip [google/
59
+ siglip-base-patch16-224](https://huggingface.co/google/siglip-base
60
+ -patch16-224) architecture.
61
+ Configuration objects inherit from [`PretrainedConfig`] and can be used
62
+ to control the model outputs. Read the documentation from
63
+ [`PretrainedConfig`] for more information.
64
+ Args:
65
+ vocab_size (`int`, *optional*, defaults to 32000):
66
+ Vocabulary size of the Siglip text model. Defines the number of
67
+ different tokens that can be represented by the `inputs_ids`
68
+ passed when calling [`SiglipModel`].
69
+ hidden_size (`int`, *optional*, defaults to 768):
70
+ Dimensionality of the encoder layers and the pooler layer.
71
+ intermediate_size (`int`, *optional*, defaults to 3072):
72
+ Dimensionality of the "intermediate" (i.e., feed-forward) layer
73
+ in the Transformer encoder.
74
+ num_hidden_layers (`int`, *optional*, defaults to 12):
75
+ Number of hidden layers in the Transformer encoder.
76
+ num_attention_heads (`int`, *optional*, defaults to 12):
77
+ Number of attention heads for each attention layer in the
78
+ Transformer encoder.
79
+ max_position_embeddings (`int`, *optional*, defaults to 64):
80
+ The maximum sequence length that this model might ever be used
81
+ with. Typically set this to something large
82
+ just in case (e.g., 512 or 1024 or 2048).
83
+ hidden_act (`str` or `function`, *optional*, defaults to
84
+ `"gelu_pytorch_tanh"`):
85
+ The non-linear activation function (function or string) in the
86
+ encoder and pooler. If string, `"gelu"`,
87
+ `"relu"`, `"selu"` and `"gelu_new"` `"quick_gelu"` are supported.
88
+ layer_norm_eps (`float`, *optional*, defaults to 1e-06):
89
+ The epsilon used by the layer normalization layers.
90
+ attention_dropout (`float`, *optional*, defaults to 0.0):
91
+ The dropout ratio for the attention probabilities.
92
+ pad_token_id (`int`, *optional*, defaults to 1):
93
+ The id of the padding token in the vocabulary.
94
+ bos_token_id (`int`, *optional*, defaults to 49406):
95
+ The id of the beginning-of-sequence token in the vocabulary.
96
+ eos_token_id (`int`, *optional*, defaults to 49407):
97
+ The id of the end-of-sequence token in the vocabulary.
98
+ Example:
99
+ ```python
100
+ >>> from transformers import SiglipTextConfig, SiglipTextModel
101
+ >>> # Initializing a SiglipTextConfig with google/siglip-base-patch16-224
102
+ style configuration
103
+ >>> configuration = SiglipTextConfig()
104
+ >>> # Initializing a SiglipTextModel (with random weights) from the
105
+ google/siglip-base-patch16-224 style configuration
106
+ >>> model = SiglipTextModel(configuration)
107
+ >>> # Accessing the model configuration
108
+ >>> configuration = model.config
109
+ ```"""
110
+
111
+ model_type = "siglip_text_model"
112
+
113
+ def __init__(
114
+ self,
115
+ vocab_size=32000,
116
+ hidden_size=768,
117
+ intermediate_size=3072,
118
+ num_hidden_layers=12,
119
+ num_attention_heads=12,
120
+ max_position_embeddings=64,
121
+ hidden_act="gelu_pytorch_tanh",
122
+ layer_norm_eps=1e-6,
123
+ attention_dropout=0.0,
124
+ # This differs from `CLIPTokenizer`'s default and from openai/siglip
125
+ # See https://github.com/huggingface/transformers/pull/24773#
126
+ # issuecomment-1632287538
127
+ pad_token_id=1,
128
+ bos_token_id=49406,
129
+ eos_token_id=49407,
130
+ _flash_attn_2_enabled=True,
131
+ **kwargs,
132
+ ):
133
+ super().__init__(pad_token_id=pad_token_id,
134
+ bos_token_id=bos_token_id,
135
+ eos_token_id=eos_token_id,
136
+ **kwargs)
137
+
138
+ self.vocab_size = vocab_size
139
+ self.hidden_size = hidden_size
140
+ self.intermediate_size = intermediate_size
141
+ self.num_hidden_layers = num_hidden_layers
142
+ self.num_attention_heads = num_attention_heads
143
+ self.max_position_embeddings = max_position_embeddings
144
+ self.layer_norm_eps = layer_norm_eps
145
+ self.hidden_act = hidden_act
146
+ self.attention_dropout = attention_dropout
147
+ self._flash_attn_2_enabled = _flash_attn_2_enabled
148
+
149
+ @classmethod
150
+ def from_pretrained(cls, pretrained_model_name_or_path: Union[str,
151
+ os.PathLike],
152
+ **kwargs) -> "PretrainedConfig":
153
+ cls._set_token_in_kwargs(kwargs)
154
+
155
+ config_dict, kwargs = cls.get_config_dict(
156
+ pretrained_model_name_or_path, **kwargs)
157
+
158
+ # get the text config dict if we are loading from SiglipConfig
159
+ if config_dict.get("model_type") == "siglip":
160
+ config_dict = config_dict["text_config"]
161
+
162
+ if "model_type" in config_dict and hasattr(
163
+ cls,
164
+ "model_type") and config_dict["model_type"] != cls.model_type:
165
+ logger.warning(
166
+ "You are using a model of type %s to instantiate a model of "
167
+ "type %s. This is not supported for all configurations of "
168
+ "models and can yield errors.", config_dict['model_type'],
169
+ cls.model_type)
170
+
171
+ return cls.from_dict(config_dict, **kwargs)
172
+
173
+
174
+class SiglipVisionConfig(PretrainedConfig):
175
+ r"""
176
+ This is the configuration class to store the configuration of a
177
+ [`SiglipVisionModel`]. It is used to instantiate a
178
+ Siglip vision encoder according to the specified arguments, defining the
179
+ model architecture. Instantiating a configuration with the defaults will
180
+ yield a similar configuration to that of the vision encoder of the Siglip
181
+ [google/siglip-base-patch16-224](https://huggingface.co/google/
182
+ siglip-base-patch16-224) architecture.
183
+ Configuration objects inherit from [`PretrainedConfig`] and can be used
184
+ to control the model outputs. Read the
185
+ documentation from [`PretrainedConfig`] for more information.
186
+ Args:
187
+ hidden_size (`int`, *optional*, defaults to 768):
188
+ Dimensionality of the encoder layers and the pooler layer.
189
+ intermediate_size (`int`, *optional*, defaults to 3072):
190
+ Dimensionality of the "intermediate" (i.e., feed-forward) layer
191
+ in the Transformer encoder.
192
+ num_hidden_layers (`int`, *optional*, defaults to 12):
193
+ Number of hidden layers in the Transformer encoder.
194
+ num_attention_heads (`int`, *optional*, defaults to 12):
195
+ Number of attention heads for each attention layer in the
196
+ Transformer encoder.
197
+ num_channels (`int`, *optional*, defaults to 3):
198
+ Number of channels in the input images.
199
+ image_size (`int`, *optional*, defaults to 224):
200
+ The size (resolution) of each image.
201
+ patch_size (`int`, *optional*, defaults to 16):
202
+ The size (resolution) of each patch.
203
+ hidden_act (`str` or `function`, *optional*, defaults to
204
+ `"gelu_pytorch_tanh"`):
205
+ The non-linear activation function (function or string) in the
206
+ encoder and pooler. If string, `"gelu"`, `"relu"`, `"selu"` and
207
+ `"gelu_new"` ``"quick_gelu"` are supported.
208
+ layer_norm_eps (`float`, *optional*, defaults to 1e-06):
209
+ The epsilon used by the layer normalization layers.
210
+ attention_dropout (`float`, *optional*, defaults to 0.0):
211
+ The dropout ratio for the attention probabilities.
212
+ Example:
213
+ ```python
214
+ >>> from transformers import SiglipVisionConfig, SiglipVisionModel
215
+ >>> # Initializing a SiglipVisionConfig with google/siglip-base-patch16-224
216
+ style configuration
217
+ >>> configuration = SiglipVisionConfig()
218
+ >>> # Initializing a SiglipVisionModel (with random weights) from the
219
+ google/siglip-base-patch16-224 style configuration
220
+ >>> model = SiglipVisionModel(configuration)
221
+ >>> # Accessing the model configuration
222
+ >>> configuration = model.config
223
+ ```"""
224
+
225
+ model_type = "siglip_vision_model"
226
+
227
+ def __init__(
228
+ self,
229
+ hidden_size=768,
230
+ intermediate_size=3072,
231
+ num_hidden_layers=12,
232
+ num_attention_heads=12,
233
+ num_channels=3,
234
+ image_size=224,
235
+ patch_size=16,
236
+ hidden_act="gelu_pytorch_tanh",
237
+ layer_norm_eps=1e-6,
238
+ attention_dropout=0.0,
239
+ _flash_attn_2_enabled=True,
240
+ **kwargs,
241
+ ):
242
+ super().__init__(**kwargs)
243
+
244
+ self.hidden_size = hidden_size
245
+ self.intermediate_size = intermediate_size
246
+ self.num_hidden_layers = num_hidden_layers
247
+ self.num_attention_heads = num_attention_heads
248
+ self.num_channels = num_channels
249
+ self.patch_size = patch_size
250
+ self.image_size = image_size
251
+ self.attention_dropout = attention_dropout
252
+ self.layer_norm_eps = layer_norm_eps
253
+ self.hidden_act = hidden_act
254
+ self._flash_attn_2_enabled = _flash_attn_2_enabled
255
+
256
+ @classmethod
257
+ def from_pretrained(cls, pretrained_model_name_or_path: Union[str,
258
+ os.PathLike],
259
+ **kwargs) -> "PretrainedConfig":
260
+ cls._set_token_in_kwargs(kwargs)
261
+
262
+ config_dict, kwargs = cls.get_config_dict(
263
+ pretrained_model_name_or_path, **kwargs)
264
+
265
+ # get the vision config dict if we are loading from SiglipConfig
266
+ if config_dict.get("model_type") == "siglip":
267
+ config_dict = config_dict["vision_config"]
268
+
269
+ if "model_type" in config_dict and hasattr(
270
+ cls,
271
+ "model_type") and config_dict["model_type"] != cls.model_type:
272
+ logger.warning(
273
+ "You are using a model of type %s to "
274
+ "instantiate a model of type %s. This is not"
275
+ " supported for all configurations of models and can yield"
276
+ " errors.", config_dict['model_type'], cls.model_type)
277
+
278
+ return cls.from_dict(config_dict, **kwargs)
279
+
280
+
281
+class SiglipConfig(PretrainedConfig):
282
+ r"""
283
+ [`SiglipConfig`] is the configuration class to store the configuration of a
284
+ [`SiglipModel`]. It is used to instantiate a Siglip model according to the
285
+ specified arguments, defining the text model and vision model configs.
286
+ Instantiating a configuration with the defaults will yield a similar
287
+ configuration to that of the Siglip [google/siglip-base-patch16-224](
288
+ https://huggingface.co/google/siglip-base-patch16-224) architecture.
289
+ Configuration objects inherit from [`PretrainedConfig`] and can be used to
290
+ control the model outputs. Read the documentation from
291
+ [`PretrainedConfig`] for more information.
292
+ Args:
293
+ text_config (`dict`, *optional*):
294
+ Dictionary of configuration options used to initialize
295
+ [`SiglipTextConfig`].
296
+ vision_config (`dict`, *optional*):
297
+ Dictionary of configuration options used to initialize
298
+ [`SiglipVisionConfig`].
299
+ kwargs (*optional*):
300
+ Dictionary of keyword arguments.
301
+ Example:
302
+ ```python
303
+ >>> from transformers import SiglipConfig, SiglipModel
304
+ >>> # Initializing a SiglipConfig with google/siglip-base-patch16-224
305
+ style configuration
306
+ >>> configuration = SiglipConfig()
307
+ >>> # Initializing a SiglipModel (with random weights) from the
308
+ google/siglip-base-patch16-224 style configuration
309
+ >>> model = SiglipModel(configuration)
310
+ >>> # Accessing the model configuration
311
+ >>> configuration = model.config
312
+ >>> # We can also initialize a SiglipConfig from a SiglipTextConfig
313
+ and a SiglipVisionConfig
314
+ >>> from transformers import SiglipTextConfig, SiglipVisionConfig
315
+ >>> # Initializing a SiglipText and SiglipVision configuration
316
+ >>> config_text = SiglipTextConfig()
317
+ >>> config_vision = SiglipVisionConfig()
318
+ >>> config = SiglipConfig.from_text_vision_configs(config_text,
319
+ config_vision)
320
+ ```"""
321
+
322
+ model_type = "siglip"
323
+
324
+ def __init__(self, text_config=None, vision_config=None, **kwargs):
325
+ super().__init__(**kwargs)
326
+
327
+ if text_config is None:
328
+ text_config = {}
329
+ logger.info(
330
+ "`text_config` is `None`. Initializing the `SiglipTextConfig`"
331
+ " with default values.")
332
+
333
+ if vision_config is None:
334
+ vision_config = {}
335
+ logger.info("`vision_config` is `None`. initializing the "
336
+ "`SiglipVisionConfig` with default values.")
337
+
338
+ self.text_config = SiglipTextConfig(**text_config)
339
+ self.vision_config = SiglipVisionConfig(**vision_config)
340
+
341
+ self.initializer_factor = 1.0
342
+
343
+ @classmethod
344
+ def from_text_vision_configs(cls, text_config: SiglipTextConfig,
345
+ vision_config: SiglipVisionConfig, **kwargs):
346
+ r"""
347
+ Instantiate a [`SiglipConfig`] (or a derived class) from siglip text
348
+ model configuration and siglip vision
349
+ model configuration.
350
+ Returns:
351
+ [`SiglipConfig`]: An instance of a configuration object
352
+ """
353
+
354
+ return cls(text_config=text_config.to_dict(),
355
+ vision_config=vision_config.to_dict(),
356
+ **kwargs)
357
+
358
+
359
+# coding=utf-8
360
+# Copyright 2024 Google AI and The HuggingFace Team. All rights reserved.
361
+#
362
+# Licensed under the Apache License, Version 2.0 (the "License");
363
+# you may not use this file except in compliance with the License.
364
+# You may obtain a copy of the License at
365
+#
366
+# http://www.apache.org/licenses/LICENSE-2.0
367
+#
368
+# Unless required by applicable law or agreed to in writing, software
369
+# distributed under the License is distributed on an "AS IS" BASIS,
370
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
371
+# See the License for the specific language governing permissions and
372
+# limitations under the License.
373
+""" PyTorch Siglip model."""
374
+
375
+_CHECKPOINT_FOR_DOC = "google/siglip-base-patch16-224"
376
+
377
+SIGLIP_PRETRAINED_MODEL_ARCHIVE_LIST = [
378
+ "google/siglip-base-patch16-224",
379
+ # See all SigLIP models at https://huggingface.co/models?filter=siglip
380
+]
381
+
382
+
383
+# Copied from transformers.models.llama.modeling_llama._get_unpad_data
384
+def _get_unpad_data(attention_mask):
385
+ seqlens_in_batch = attention_mask.sum(dim=-1, dtype=torch.int32)
386
+ indices = torch.nonzero(attention_mask.flatten(), as_tuple=False).flatten()
387
+ max_seqlen_in_batch = seqlens_in_batch.max().item()
388
+ cu_seqlens = F.pad(
389
+ torch.cumsum(seqlens_in_batch, dim=0, dtype=torch.torch.int32), (1, 0))
390
+ return (
391
+ indices,
392
+ cu_seqlens,
393
+ max_seqlen_in_batch,
394
+ )
395
+
396
+
397
+def _trunc_normal_(tensor, mean, std, a, b):
398
+ # Cut & paste from PyTorch official master until it's in a few official
399
+ # releases - RW
400
+ # Method based on https://people.sc.fsu.edu/~jburkardt/presentations/
401
+ # truncated_normal.pdf
402
+ def norm_cdf(x):
403
+ # Computes standard normal cumulative distribution function
404
+ return (1.0 + math.erf(x / math.sqrt(2.0))) / 2.0
405
+
406
+ if (mean < a - 2 * std) or (mean > b + 2 * std):
407
+ warnings.warn(
408
+ "mean is more than 2 std from [a, b] in nn.init.trunc_normal_. "
409
+ "The distribution of values may be incorrect.",
410
+ stacklevel=2,
411
+ )
412
+
413
+ # Values are generated by using a truncated uniform distribution and
414
+ # then using the inverse CDF for the normal distribution.
415
+ # Get upper and lower cdf values
416
+ l = norm_cdf((a - mean) / std) # noqa
417
+ u = norm_cdf((b - mean) / std) # noqa
418
+
419
+ # Uniformly fill tensor with values from [l, u], then translate to
420
+ # [2l-1, 2u-1].
421
+ tensor.uniform_(2 * l - 1, 2 * u - 1)
422
+
423
+ # Use inverse cdf transform for normal distribution to get truncated
424
+ # standard normal
425
+ if tensor.dtype in [torch.float16, torch.bfloat16]:
426
+ # The `erfinv_` op is not (yet?) defined in float16+cpu, bfloat16+gpu
427
+ og_dtype = tensor.dtype
428
+ tensor = tensor.to(torch.float32)
429
+ tensor.erfinv_()
430
+ tensor = tensor.to(og_dtype)
431
+ else:
432
+ tensor.erfinv_()
433
+
434
+ # Transform to proper mean, std
435
+ tensor.mul_(std * math.sqrt(2.0))
436
+ tensor.add_(mean)
437
+
438
+ # Clamp to ensure it's in the proper range
439
+ if tensor.dtype == torch.float16:
440
+ # The `clamp_` op is not (yet?) defined in float16+cpu
441
+ tensor = tensor.to(torch.float32)
442
+ tensor.clamp_(min=a, max=b)
443
+ tensor = tensor.to(torch.float16)
444
+ else:
445
+ tensor.clamp_(min=a, max=b)
446
+
447
+
448
+def trunc_normal_tf_(tensor: torch.Tensor,
449
+ mean: float = 0.0,
450
+ std: float = 1.0,
451
+ a: float = -2.0,
452
+ b: float = 2.0) -> torch.Tensor:
453
+ """Fills the input Tensor with values drawn from a truncated
454
+ normal distribution. The values are effectively drawn from the
455
+ normal distribution :math:`\\mathcal{N}(\text{mean}, \text{std}^2)`
456
+ with values outside :math:`[a, b]` redrawn until they are within
457
+ the bounds. The method used for generating the random values works
458
+ best when :math:`a \\leq \text{mean} \\leq b`.
459
+ NOTE: this 'tf' variant behaves closer to Tensorflow / JAX impl where
460
+ the bounds [a, b] are applied when sampling the normal distribution with
461
+ mean=0, std=1.0 and the result is subsequently scaled and shifted by the
462
+ mean and std args.
463
+ Args:
464
+ tensor: an n-dimensional `torch.Tensor`
465
+ mean: the mean of the normal distribution
466
+ std: the standard deviation of the normal distribution
467
+ a: the minimum cutoff value
468
+ b: the maximum cutoff value
469
+ """
470
+ with torch.no_grad():
471
+ _trunc_normal_(tensor, 0, 1.0, a, b)
472
+ tensor.mul_(std).add_(mean)
473
+
474
+
475
+def variance_scaling_(tensor, scale=1.0, mode="fan_in", distribution="normal"):
476
+ fan_in, fan_out = _calculate_fan_in_and_fan_out(tensor)
477
+ if mode == "fan_in":
478
+ denom = fan_in
479
+ elif mode == "fan_out":
480
+ denom = fan_out
481
+ elif mode == "fan_avg":
482
+ denom = (fan_in + fan_out) / 2
483
+
484
+ variance = scale / denom
485
+
486
+ if distribution == "truncated_normal":
487
+ # constant is stddev of standard normal truncated to (-2, 2)
488
+ trunc_normal_tf_(tensor, std=math.sqrt(variance) / 0.87962566103423978)
489
+ elif distribution == "normal":
490
+ with torch.no_grad():
491
+ tensor.normal_(std=math.sqrt(variance))
492
+ elif distribution == "uniform":
493
+ bound = math.sqrt(3 * variance)
494
+ with torch.no_grad():
495
+ tensor.uniform_(-bound, bound)
496
+ else:
497
+ raise ValueError(f"invalid distribution {distribution}")
498
+
499
+
500
+def lecun_normal_(tensor):
501
+ variance_scaling_(tensor, mode="fan_in", distribution="truncated_normal")
502
+
503
+
504
+def default_flax_embed_init(tensor):
505
+ variance_scaling_(tensor, mode="fan_in", distribution="normal")
506
+
507
+
508
+@dataclass
509
+# Copied from transformers.models.clip.modeling_clip.CLIPVisionModelOutput with
510
+# CLIP->Siglip
511
+class SiglipVisionModelOutput(ModelOutput):
512
+ """
513
+ Base class for vision model's outputs that also contains image embeddings
514
+ of the pooling of the last hidden states.
515
+ Args:
516
+ image_embeds (`torch.FloatTensor` of shape `(batch_size, output_dim)`
517
+ *optional* returned when model is initialized with
518
+ `with_projection=True`):
519
+ The image embeddings obtained by applying the projection layer to
520
+ the pooler_output.
521
+ last_hidden_state (`torch.FloatTensor` of shape `(batch_size,
522
+ sequence_length, hidden_size)`):
523
+ Sequence of hidden-states at the output of the last layer of the
524
+ model.
525
+ hidden_states (`tuple(torch.FloatTensor)`, *optional*, returned when
526
+ `output_hidden_states=True` is passed or when
527
+ `config.output_hidden_states=True`):
528
+ Tuple of `torch.FloatTensor` (one for the output of the embeddings,
529
+ if the model has an embedding layer, + one for the output of each
530
+ layer) of shape `(batch_size, sequence_length, hidden_size)`.
531
+ Hidden-states of the model at the output of each layer plus the
532
+ optional initial embedding outputs.
533
+ attentions (`tuple(torch.FloatTensor)`, *optional*, returned when
534
+ `output_attentions=True` is passed or when
535
+ `config.output_attentions=True`):
536
+ Tuple of `torch.FloatTensor` (one for each layer) of shape
537
+ `(batch_size, num_heads, sequence_length, sequence_length)`.
538
+ Attentions weights after the attention softmax, used to compute the
539
+ weighted average in the self-attention heads.
540
+ """
541
+
542
+ image_embeds: Optional[torch.FloatTensor] = None
543
+ last_hidden_state: torch.FloatTensor = None
544
+ hidden_states: Optional[Tuple[torch.FloatTensor]] = None
545
+ attentions: Optional[Tuple[torch.FloatTensor]] = None
546
+
547
+
548
+@dataclass
549
+# Copied from transformers.models.clip.modeling_clip.CLIPTextModelOutput with
550
+# CLIP->Siglip
551
+class SiglipTextModelOutput(ModelOutput):
552
+ """
553
+ Base class for text model's outputs that also contains a pooling of the
554
+ last hidden states.
555
+ Args:
556
+ text_embeds (`torch.FloatTensor` of shape `(batch_size, output_dim)`
557
+ *optional* returned when model is initialized with
558
+ `with_projection=True`):
559
+ The text embeddings obtained by applying the projection layer to
560
+ model.
561
+ the pooler_output.
562
+ last_hidden_state (`torch.FloatTensor` of shape `(batch_size,
563
+ sequence_length, hidden_size)`):
564
+ Sequence of hidden-states at the output of the last layer of the
565
+ hidden_states (`tuple(torch.FloatTensor)`, *optional*, returned when
566
+ `output_hidden_states=True` is passed or when
567
+ `config.output_hidden_states=True`):
568
+ Tuple of `torch.FloatTensor` (one for the output of the
569
+ embeddings, if the model has an embedding layer, + one for the
570
+ output of each layer) of shape `(batch_size, sequence_length,
571
+ hidden_size)`.
572
+ Hidden-states of the model at the output of each layer plus the
573
+ optional initial embedding outputs.
574
+ attentions (`tuple(torch.FloatTensor)`, *optional*, returned when
575
+ `output_attentions=True` is passed or when
576
+ `config.output_attentions=True`):
577
+ Tuple of `torch.FloatTensor` (one for each layer) of shape
578
+ `(batch_size, num_heads, sequence_length, sequence_length)`.
579
+ Attentions weights after the attention softmax, used to compute
580
+ the weighted average in the self-attention heads.
581
+ """
582
+
583
+ text_embeds: Optional[torch.FloatTensor] = None
584
+ last_hidden_state: torch.FloatTensor = None
585
+ hidden_states: Optional[Tuple[torch.FloatTensor]] = None
586
+ attentions: Optional[Tuple[torch.FloatTensor]] = None
587
+
588
+
589
+@dataclass
590
+# Copied from transformers.models.clip.modeling_clip.CLIPOutput with
591
+# CLIP->Siglip
592
+class SiglipOutput(ModelOutput):
593
+ """
594
+ Args:
595
+ loss (`torch.FloatTensor` of shape `(1,)`, *optional*, returned when
596
+ `return_loss` is `True`):
597
+ Contrastive loss for image-text similarity.
598
+ logits_per_image:(`torch.FloatTensor` of shape `(image_batch_size,
599
+ text_batch_size)`):
600
+ The scaled dot product scores between `image_embeds` and
601
+ `text_embeds`. This represents the image-text similarity scores.
602
+ logits_per_text:(`torch.FloatTensor` of shape `(text_batch_size,
603
+ image_batch_size)`):
604
+ The scaled dot product scores between `text_embeds` and
605
+ `image_embeds`. This represents the text-image similarity scores.
606
+ text_embeds(`torch.FloatTensor` of shape `(batch_size, output_dim`):
607
+ The text embeddings obtained by applying the projection layer to
608
+ the pooled output of [`SiglipTextModel`].
609
+ image_embeds(`torch.FloatTensor` of shape `(batch_size, output_dim`):
610
+ The image embeddings obtained by applying the projection layer to
611
+ the pooled output of [`SiglipVisionModel`].
612
+ text_model_output(`BaseModelOutputWithPooling`):
613
+ The output of the [`SiglipTextModel`].
614
+ vision_model_output(`BaseModelOutputWithPooling`):
615
+ The output of the [`SiglipVisionModel`].
616
+ """
617
+
618
+ loss: Optional[torch.FloatTensor] = None
619
+ logits_per_image: torch.FloatTensor = None
620
+ logits_per_text: torch.FloatTensor = None
621
+ text_embeds: torch.FloatTensor = None
622
+ image_embeds: torch.FloatTensor = None
623
+ text_model_output: BaseModelOutputWithPooling = None
624
+ vision_model_output: BaseModelOutputWithPooling = None
625
+
626
+ def to_tuple(self) -> Tuple[Any]:
627
+ return tuple(
628
+ self[k] if k not in ["text_model_output", "vision_model_output"
629
+ ] else getattr(self, k).to_tuple()
630
+ for k in self.keys())
631
+
632
+
633
+class SiglipVisionEmbeddings(nn.Module):
634
+
635
+ def __init__(self, config: SiglipVisionConfig):
636
+ super().__init__()
637
+ self.config = config
638
+ self.embed_dim = config.hidden_size
639
+ self.image_size = config.image_size
640
+ self.patch_size = config.patch_size
641
+
642
+ self.patch_embedding = nn.Conv2d(
643
+ in_channels=config.num_channels,
644
+ out_channels=self.embed_dim,
645
+ kernel_size=self.patch_size,
646
+ stride=self.patch_size,
647
+ padding="valid",
648
+ )
649
+
650
+ self.num_patches_per_side = self.image_size // self.patch_size
651
+ self.num_patches = self.num_patches_per_side**2
652
+ self.num_positions = self.num_patches
653
+ self.position_embedding = nn.Embedding(self.num_positions,
654
+ self.embed_dim)
655
+
656
+ def forward(self, pixel_values: torch.FloatTensor,
657
+ patch_attention_mask: torch.BoolTensor) -> torch.Tensor:
658
+ batch_size = pixel_values.size(0)
659
+
660
+ patch_embeds = self.patch_embedding(pixel_values)
661
+ embeddings = patch_embeds.flatten(2).transpose(1, 2)
662
+
663
+ max_im_h, max_im_w = pixel_values.size(2), pixel_values.size(3)
664
+ max_nb_patches_h, max_nb_patches_w = max_im_h // self.patch_size, \
665
+ max_im_w // self.patch_size
666
+ boundaries = torch.arange(1 / self.num_patches_per_side, 1.0,
667
+ 1 / self.num_patches_per_side)
668
+ position_ids = torch.full(
669
+ size=(
670
+ batch_size,
671
+ max_nb_patches_h * max_nb_patches_w,
672
+ ),
673
+ fill_value=0,
674
+ )
675
+
676
+ for batch_idx, p_attn_mask in enumerate(patch_attention_mask):
677
+ nb_patches_h = p_attn_mask[:, 0].sum()
678
+ nb_patches_w = p_attn_mask[0].sum()
679
+
680
+ fractional_coords_h = torch.linspace(0, 1 - 1 / nb_patches_h,
681
+ nb_patches_h)
682
+ fractional_coords_w = torch.linspace(0, 1 - 1 / nb_patches_w,
683
+ nb_patches_w)
684
+
685
+ bucket_coords_h = torch.bucketize(fractional_coords_h,
686
+ boundaries,
687
+ right=True)
688
+ bucket_coords_w = torch.bucketize(fractional_coords_w,
689
+ boundaries,
690
+ right=True)
691
+
692
+ pos_ids = (bucket_coords_h[:, None] * self.num_patches_per_side +
693
+ bucket_coords_w).flatten()
694
+ position_ids[batch_idx][p_attn_mask.view(-1).cpu()] = pos_ids
695
+
696
+ position_ids = position_ids.to(self.position_embedding.weight.device)
697
+
698
+ embeddings = embeddings + self.position_embedding(position_ids)
699
+ return embeddings
700
+
701
+
702
+# Copied from transformers.models.clip.modeling_clip.CLIPTextEmbeddings with
703
+# CLIP->Siglip
704
+class SiglipTextEmbeddings(nn.Module):
705
+
706
+ def __init__(self, config: SiglipTextConfig):
707
+ super().__init__()
708
+ embed_dim = config.hidden_size
709
+
710
+ self.token_embedding = nn.Embedding(config.vocab_size, embed_dim)
711
+ self.position_embedding = nn.Embedding(config.max_position_embeddings,
712
+ embed_dim)
713
+
714
+ # position_ids (1, len position emb) is contiguous in memory and
715
+ # exported when serialized
716
+ self.register_buffer(
717
+ "position_ids",
718
+ torch.arange(config.max_position_embeddings).expand((1, -1)),
719
+ persistent=False)
720
+
721
+ def forward(
722
+ self,
723
+ input_ids: Optional[torch.LongTensor] = None,
724
+ position_ids: Optional[torch.LongTensor] = None,
725
+ inputs_embeds: Optional[torch.FloatTensor] = None,
726
+ ) -> torch.Tensor:
727
+ seq_length = input_ids.shape[
728
+ -1] if input_ids is not None else inputs_embeds.shape[-2]
729
+
730
+ if position_ids is None:
731
+ position_ids = self.position_ids[:, :seq_length]
732
+
733
+ if inputs_embeds is None:
734
+ inputs_embeds = self.token_embedding(input_ids)
735
+
736
+ position_embeddings = self.position_embedding(position_ids)
737
+ embeddings = inputs_embeds + position_embeddings
738
+
739
+ return embeddings
740
+
741
+
742
+class SiglipAttention(nn.Module):
743
+ """Multi-headed attention from 'Attention Is All You Need' paper"""
744
+
745
+ # Copied from transformers.models.clip.modeling_clip.CLIPAttention.__init__
746
+ def __init__(self, config):
747
+ super().__init__()
748
+ self.config = config
749
+ self.embed_dim = config.hidden_size
750
+ self.num_heads = config.num_attention_heads
751
+ self.head_dim = self.embed_dim // self.num_heads
752
+ if self.head_dim * self.num_heads != self.embed_dim:
753
+ raise ValueError(
754
+ f"embed_dim must be divisible by num_heads (got `embed_dim`:"
755
+ f" {self.embed_dim} and `num_heads`: {self.num_heads}).")
756
+ self.scale = self.head_dim**-0.5
757
+ self.dropout = config.attention_dropout
758
+
759
+ self.k_proj = nn.Linear(self.embed_dim, self.embed_dim)
760
+ self.v_proj = nn.Linear(self.embed_dim, self.embed_dim)
761
+ self.q_proj = nn.Linear(self.embed_dim, self.embed_dim)
762
+ self.out_proj = nn.Linear(self.embed_dim, self.embed_dim)
763
+
764
+ def forward(
765
+ self,
766
+ hidden_states: torch.Tensor,
767
+ attention_mask: Optional[torch.Tensor] = None,
768
+ output_attentions: Optional[bool] = False,
769
+ ) -> Tuple[torch.Tensor, Optional[torch.Tensor],
770
+ Optional[Tuple[torch.Tensor]]]:
771
+ """Input shape: Batch x Time x Channel"""
772
+
773
+ batch_size, q_len, _ = hidden_states.size()
774
+
775
+ query_states = self.q_proj(hidden_states)
776
+ key_states = self.k_proj(hidden_states)
777
+ value_states = self.v_proj(hidden_states)
778
+
779
+ query_states = query_states.view(batch_size, q_len, self.num_heads,
780
+ self.head_dim).transpose(1, 2)
781
+ key_states = key_states.view(batch_size, q_len, self.num_heads,
782
+ self.head_dim).transpose(1, 2)
783
+ value_states = value_states.view(batch_size, q_len, self.num_heads,
784
+ self.head_dim).transpose(1, 2)
785
+
786
+ k_v_seq_len = key_states.shape[-2]
787
+ attn_weights = torch.matmul(query_states, key_states.transpose(
788
+ 2, 3)) * self.scale
789
+
790
+ if attn_weights.size() != (batch_size, self.num_heads, q_len,
791
+ k_v_seq_len):
792
+ raise ValueError(
793
+ f"Attention weights should be of size "
794
+ f"{(batch_size, self.num_heads, q_len, k_v_seq_len)}, but is"
795
+ f" {attn_weights.size()}")
796
+
797
+ if attention_mask is not None:
798
+ if attention_mask.size() != (batch_size, 1, q_len, k_v_seq_len):
799
+ raise ValueError(f"Attention mask should be of size "
800
+ f"{(batch_size, 1, q_len, k_v_seq_len)}, "
801
+ f"but is {attention_mask.size()}")
802
+ attn_weights = attn_weights + attention_mask
803
+
804
+ # upcast attention to fp32
805
+ attn_weights = nn.functional.softmax(attn_weights,
806
+ dim=-1,
807
+ dtype=torch.float32).to(
808
+ query_states.dtype)
809
+ attn_weights = nn.functional.dropout(attn_weights,
810
+ p=self.dropout,
811
+ training=self.training)
812
+ attn_output = torch.matmul(attn_weights, value_states)
813
+
814
+ if attn_output.size() != (batch_size, self.num_heads, q_len,
815
+ self.head_dim):
816
+ raise ValueError(
817
+ f"`attn_output` should be of size "
818
+ f"{(batch_size, self.num_heads, q_len, self.head_dim)}, but is"
819
+ f" {attn_output.size()}")
820
+
821
+ attn_output = attn_output.transpose(1, 2).contiguous()
822
+ attn_output = attn_output.reshape(batch_size, q_len, self.embed_dim)
823
+
824
+ attn_output = self.out_proj(attn_output)
825
+
826
+ return attn_output, attn_weights
827
+
828
+
829
+class SiglipFlashAttention2(SiglipAttention):
830
+ """
831
+ Llama flash attention module. This module inherits from `LlamaAttention` as
832
+ the weights of the module stays untouched. The only required change would
833
+ be on the forward pass where it needs to correctly call the public API of
834
+ flash attention and deal with padding tokens in case the input contains any
835
+ of them.
836
+ """
837
+
838
+ def __init__(self, *args, **kwargs):
839
+ super().__init__(*args, **kwargs)
840
+ self.is_causal = False # Hack to make sure we don't use a causal mask
841
+
842
+ def forward(
843
+ self,
844
+ hidden_states: torch.Tensor,
845
+ attention_mask: Optional[torch.LongTensor] = None,
846
+ position_ids: Optional[torch.LongTensor] = None,
847
+ past_key_value: Optional[Tuple[torch.Tensor]] = None,
848
+ output_attentions: bool = False,
849
+ use_cache: bool = False,
850
+ **kwargs,
851
+ ) -> Tuple[torch.Tensor, Optional[torch.Tensor],
852
+ Optional[Tuple[torch.Tensor]]]:
853
+ output_attentions = False
854
+
855
+ bsz, q_len, _ = hidden_states.size()
856
+
857
+ query_states = self.q_proj(hidden_states)
858
+ key_states = self.k_proj(hidden_states)
859
+ value_states = self.v_proj(hidden_states)
860
+
861
+ # Flash attention requires the input to have the shape
862
+ # batch_size x seq_length x head_dim x hidden_dim
863
+ # therefore we just need to keep the original shape
864
+ query_states = query_states.view(bsz, q_len, self.num_heads,
865
+ self.head_dim).transpose(1, 2)
866
+ key_states = key_states.view(bsz, q_len, self.num_heads,
867
+ self.head_dim).transpose(1, 2)
868
+ value_states = value_states.view(bsz, q_len, self.num_heads,
869
+ self.head_dim).transpose(1, 2)
870
+
871
+ kv_seq_len = key_states.shape[-2]
872
+ if past_key_value is not None:
873
+ kv_seq_len += past_key_value.get_usable_length(
874
+ kv_seq_len, self.layer_idx)
875
+
876
+ # TODO: These transpose are quite inefficient but Flash Attention
877
+ # requires the layout [batch_size, sequence_length, num_heads,
878
+ # head_dim]. We would need to refactor the KV cache
879
+ # to be able to avoid many of these transpose/reshape/view.
880
+ query_states = query_states.transpose(1, 2)
881
+ key_states = key_states.transpose(1, 2)
882
+ value_states = value_states.transpose(1, 2)
883
+
884
+ dropout_rate = self.dropout if self.training else 0.0
885
+
886
+ # In PEFT, usually we cast the layer norms in float32 for training
887
+ # stability reasons therefore the input hidden states gets silently
888
+ # casted in float32. Hence, we need cast them back in the correct
889
+ # dtype just to be sure everything works as expected.
890
+ # This might slowdown training & inference so it is recommended to
891
+ # not cast the LayerNorms in fp32. (LlamaRMSNorm handles it correctly)
892
+
893
+ input_dtype = query_states.dtype
894
+ if input_dtype == torch.float32:
895
+ if torch.is_autocast_enabled():
896
+ target_dtype = torch.get_autocast_gpu_dtype()
897
+ # Handle the case where the model is quantized
898
+ elif hasattr(self.config, "_pre_quantization_dtype"):
899
+ target_dtype = self.config._pre_quantization_dtype
900
+ else:
901
+ target_dtype = self.q_proj.weight.dtype
902
+
903
+ logger.warning_once(
904
+ "The input hidden states seems to be silently casted in "
905
+ "float32, this might be related to the fact you have upcasted "
906
+ "embedding or layer norm layers in float32. We will cast "
907
+ f"back the input in {target_dtype}.")
908
+
909
+ query_states = query_states.to(target_dtype)
910
+ key_states = key_states.to(target_dtype)
911
+ value_states = value_states.to(target_dtype)
912
+
913
+ attn_output = self._flash_attention_forward(query_states,
914
+ key_states,
915
+ value_states,
916
+ attention_mask,
917
+ q_len,
918
+ dropout=dropout_rate)
919
+
920
+ attn_output = attn_output.reshape(bsz, q_len,
921
+ self.embed_dim).contiguous()
922
+ attn_output = self.out_proj(attn_output)
923
+
924
+ if not output_attentions:
925
+ attn_weights = None
926
+
927
+ return attn_output, attn_weights
928
+
929
+ def _flash_attention_forward(self,
930
+ query_states,
931
+ key_states,
932
+ value_states,
933
+ attention_mask,
934
+ query_length,
935
+ dropout=0.0,
936
+ softmax_scale=None):
937
+ """
938
+ Calls the forward method of Flash Attention - if the input hidden
939
+ states contain at least one padding token first unpad the input,
940
+ then computes the attention scores and pad the final attention
941
+ scores.
942
+ Args:
943
+ query_states (`torch.Tensor`):
944
+ Input query states to be passed to Flash Attention API
945
+ key_states (`torch.Tensor`):
946
+ Input key states to be passed to Flash Attention API
947
+ value_states (`torch.Tensor`):
948
+ Input value states to be passed to Flash Attention API
949
+ attention_mask (`torch.Tensor`):
950
+ The padding mask - corresponds to a tensor of size
951
+ `(batch_size, seq_len)` where 0 stands for the position
952
+ of padding tokens and 1 for the position of non-padding
953
+ tokens.
954
+ dropout (`int`, *optional*):
955
+ Attention dropout
956
+ softmax_scale (`float`, *optional*):
957
+ The scaling of QK^T before applying softmax. Default to 1 /
958
+ sqrt(head_dim)
959
+ """
960
+ from flash_attn import flash_attn_func, flash_attn_varlen_func
961
+ from flash_attn.bert_padding import pad_input # noqa
962
+
963
+ # TODO: Remove the `query_length != 1` check once Flash Attention for
964
+ # RoCm is bumped to 2.1. For details, please see the comment in
965
+ # LlamaFlashAttention2 __init__.
966
+ causal = self.is_causal and query_length != 1
967
+
968
+ # Contains at least one padding token in the sequence
969
+ if attention_mask is not None:
970
+ batch_size = query_states.shape[0]
971
+ query_states, key_states, value_states, indices_q, cu_seq_lens, \
972
+ max_seq_lens = self._upad_input(
973
+ query_states, key_states, value_states, attention_mask,
974
+ query_length)
975
+
976
+ cu_seqlens_q, cu_seqlens_k = cu_seq_lens
977
+ max_seqlen_in_batch_q, max_seqlen_in_batch_k = max_seq_lens
978
+
979
+ attn_output_unpad = flash_attn_varlen_func(
980
+ query_states,
981
+ key_states,
982
+ value_states,
983
+ cu_seqlens_q=cu_seqlens_q,
984
+ cu_seqlens_k=cu_seqlens_k,
985
+ max_seqlen_q=max_seqlen_in_batch_q,
986
+ max_seqlen_k=max_seqlen_in_batch_k,
987
+ dropout_p=dropout,
988
+ softmax_scale=softmax_scale,
989
+ causal=causal,
990
+ )
991
+
992
+ attn_output = pad_input(attn_output_unpad, indices_q, batch_size,
993
+ query_length)
994
+ else:
995
+ attn_output = flash_attn_func(query_states,
996
+ key_states,
997
+ value_states,
998
+ dropout,
999
+ softmax_scale=softmax_scale,
1000
+ causal=causal)
1001
+
1002
+ return attn_output
1003
+
1004
+ def _upad_input(self, query_layer, key_layer, value_layer, attention_mask,
1005
+ query_length):
1006
+ from flash_attn.bert_padding import index_first_axis, unpad_input
1007
+ indices_k, cu_seqlens_k, max_seqlen_in_batch_k = _get_unpad_data(
1008
+ attention_mask)
1009
+ batch_size, kv_seq_len, num_key_value_heads, head_dim = key_layer.shape
1010
+
1011
+ key_layer = index_first_axis(
1012
+ key_layer.reshape(batch_size * kv_seq_len, num_key_value_heads,
1013
+ head_dim), indices_k)
1014
+ value_layer = index_first_axis(
1015
+ value_layer.reshape(batch_size * kv_seq_len, num_key_value_heads,
1016
+ head_dim), indices_k)
1017
+ if query_length == kv_seq_len:
1018
+ query_layer = index_first_axis(
1019
+ query_layer.reshape(batch_size * kv_seq_len, self.num_heads,
1020
+ head_dim), indices_k)
1021
+ cu_seqlens_q = cu_seqlens_k
1022
+ max_seqlen_in_batch_q = max_seqlen_in_batch_k
1023
+ indices_q = indices_k
1024
+ elif query_length == 1:
1025
+ max_seqlen_in_batch_q = 1
1026
+ cu_seqlens_q = torch.arange(
1027
+ batch_size + 1, dtype=torch.int32, device=query_layer.device
1028
+ ) # There is a memcpy here, that is very bad.
1029
+ indices_q = cu_seqlens_q[:-1]
1030
+ query_layer = query_layer.squeeze(1)
1031
+ else:
1032
+ # The -q_len: slice assumes left padding.
1033
+ attention_mask = attention_mask[:, -query_length:]
1034
+ query_layer, indices_q, cu_seqlens_q, max_seqlen_in_batch_q = \
1035
+ unpad_input(query_layer, attention_mask)
1036
+
1037
+ return (
1038
+ query_layer,
1039
+ key_layer,
1040
+ value_layer,
1041
+ indices_q,
1042
+ (cu_seqlens_q, cu_seqlens_k),
1043
+ (max_seqlen_in_batch_q, max_seqlen_in_batch_k),
1044
+ )
1045
+
1046
+
1047
+# Copied from transformers.models.clip.modeling_clip.CLIPMLP with CLIP->Siglip
1048
+class SiglipMLP(nn.Module):
1049
+
1050
+ def __init__(self, config):
1051
+ super().__init__()
1052
+ self.config = config
1053
+ self.activation_fn = ACT2FN[config.hidden_act]
1054
+ self.fc1 = nn.Linear(config.hidden_size, config.intermediate_size)
1055
+ self.fc2 = nn.Linear(config.intermediate_size, config.hidden_size)
1056
+
1057
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
1058
+ hidden_states = self.fc1(hidden_states)
1059
+ hidden_states = self.activation_fn(hidden_states)
1060
+ hidden_states = self.fc2(hidden_states)
1061
+ return hidden_states
1062
+
1063
+
1064
+# Copied from transformers.models.clip.modeling_clip.CLIPEncoderLayer with
1065
+# CLIP->Siglip
1066
+class SiglipEncoderLayer(nn.Module):
1067
+
1068
+ def __init__(self, config: SiglipConfig):
1069
+ super().__init__()
1070
+ self.embed_dim = config.hidden_size
1071
+ self.self_attn = (SiglipAttention(config) if
1072
+ not getattr(config, "_flash_attn_2_enabled", False)
1073
+ else SiglipFlashAttention2(config))
1074
+ self.layer_norm1 = nn.LayerNorm(self.embed_dim,
1075
+ eps=config.layer_norm_eps)
1076
+ self.mlp = SiglipMLP(config)
1077
+ self.layer_norm2 = nn.LayerNorm(self.embed_dim,
1078
+ eps=config.layer_norm_eps)
1079
+
1080
+ def forward(
1081
+ self,
1082
+ hidden_states: torch.Tensor,
1083
+ attention_mask: torch.Tensor,
1084
+ output_attentions: Optional[bool] = False,
1085
+ ) -> Tuple[torch.FloatTensor]:
1086
+ """
1087
+ Args:
1088
+ hidden_states (`torch.FloatTensor`):
1089
+ Input to the layer of shape `(batch, seq_len, embed_dim)`.
1090
+ attention_mask (`torch.FloatTensor`):
1091
+ Attention mask of shape `(batch, 1, q_len, k_v_seq_len)` where
1092
+ padding elements are indicated by very large negative values.
1093
+ output_attentions (`bool`, *optional*, defaults to `False`):
1094
+ Whether or not to return the attentions tensors of all
1095
+ attention layers. See `attentions` under returned tensors for
1096
+ more detail.
1097
+ """
1098
+ residual = hidden_states
1099
+
1100
+ hidden_states = self.layer_norm1(hidden_states)
1101
+ hidden_states, attn_weights = self.self_attn(
1102
+ hidden_states=hidden_states,
1103
+ attention_mask=attention_mask,
1104
+ output_attentions=output_attentions,
1105
+ )
1106
+ hidden_states = residual + hidden_states
1107
+
1108
+ residual = hidden_states
1109
+ hidden_states = self.layer_norm2(hidden_states)
1110
+ hidden_states = self.mlp(hidden_states)
1111
+ hidden_states = residual + hidden_states
1112
+
1113
+ outputs = (hidden_states, )
1114
+
1115
+ if output_attentions:
1116
+ outputs += (attn_weights, )
1117
+
1118
+ return outputs
1119
+
1120
+
1121
+class SiglipPreTrainedModel(PreTrainedModel):
1122
+ """
1123
+ An abstract class to handle weights initialization and a simple interface
1124
+ for downloading and loading pretrained models.
1125
+ """
1126
+
1127
+ config_class = SiglipConfig
1128
+ base_model_prefix = "siglip"
1129
+ supports_gradient_checkpointing = True
1130
+
1131
+ def _init_weights(self, module):
1132
+ """Initialize the weights"""
1133
+
1134
+ if isinstance(module, SiglipVisionEmbeddings):
1135
+ width = (self.config.vision_config.hidden_size if isinstance(
1136
+ self.config, SiglipConfig) else self.config.hidden_size)
1137
+ nn.init.normal_(module.position_embedding.weight,
1138
+ std=1 / np.sqrt(width))
1139
+ elif isinstance(module, nn.Embedding):
1140
+ default_flax_embed_init(module.weight)
1141
+ elif isinstance(module, SiglipAttention):
1142
+ nn.init.normal_(module.q_proj.weight)
1143
+ nn.init.normal_(module.k_proj.weight)
1144
+ nn.init.normal_(module.v_proj.weight)
1145
+ nn.init.normal_(module.out_proj.weight)
1146
+ nn.init.zeros_(module.q_proj.bias)
1147
+ nn.init.zeros_(module.k_proj.bias)
1148
+ nn.init.zeros_(module.v_proj.bias)
1149
+ nn.init.zeros_(module.out_proj.bias)
1150
+ elif isinstance(module, SiglipMLP):
1151
+ nn.init.normal_(module.fc1.weight)
1152
+ nn.init.normal_(module.fc2.weight)
1153
+ nn.init.normal_(module.fc1.bias, std=1e-6)
1154
+ nn.init.normal_(module.fc2.bias, std=1e-6)
1155
+ elif isinstance(module, SiglipMultiheadAttentionPoolingHead):
1156
+ nn.init.normal_(module.probe.data)
1157
+ nn.init.normal_(module.attention.in_proj_weight.data)
1158
+ nn.init.zeros_(module.attention.in_proj_bias.data)
1159
+ elif isinstance(module, SiglipModel):
1160
+ logit_scale_init = torch.tensor(0.0)
1161
+ module.logit_scale.data.fill_(logit_scale_init)
1162
+ module.logit_bias.data.zero_()
1163
+ elif isinstance(module, (nn.Linear, nn.Conv2d)):
1164
+ lecun_normal_(module.weight)
1165
+ if module.bias is not None:
1166
+ nn.init.zeros_(module.bias)
1167
+ elif isinstance(module, nn.LayerNorm):
1168
+ module.bias.data.zero_()
1169
+ module.weight.data.fill_(1.0)
1170
+
1171
+
1172
+SIGLIP_START_DOCSTRING = r"""
1173
+ This model inherits from [`PreTrainedModel`]. Check the superclass
1174
+ documentation for the generic methods the library implements for all
1175
+ its model (such as downloading or saving, resizing the input embeddings,
1176
+ pruning heads etc.)
1177
+ This model is also a PyTorch [torch.nn.Module](https://pytorch.org/docs/
1178
+ stable/nn.html#torch.nn.Module) subclass.
1179
+ Use it as a regular PyTorch Module and refer to the PyTorch documentation
1180
+ for all matter related to general usage and behavior.
1181
+ Parameters:
1182
+ config ([`SiglipConfig`]): Model configuration class with all the
1183
+ parameters of the model.
1184
+ Initializing with a config file does not load the weights
1185
+ associated with the model, only the configuration. Check out
1186
+ the [`~PreTrainedModel.from_pretrained`] method to load the
1187
+ model weights.
1188
+"""
1189
+
1190
+SIGLIP_TEXT_INPUTS_DOCSTRING = r"""
1191
+ Args:
1192
+ input_ids (`torch.LongTensor` of shape `(batch_size, sequence_length)
1193
+ `):
1194
+ Indices of input sequence tokens in the vocabulary. Padding will
1195
+ be ignored by default should you provide it.
1196
+ Indices can be obtained using [`AutoTokenizer`]. See
1197
+ [`PreTrainedTokenizer.encode`] and [`PreTrainedTokenizer.__call__`]
1198
+ for details. [What are input IDs?](../glossary#input-ids)
1199
+ attention_mask (`torch.Tensor` of shape `(batch_size,
1200
+ sequence_length)`, *optional*):
1201
+ Mask to avoid performing attention on padding token indices. Mask
1202
+ values selected in `[0, 1]`:
1203
+ - 1 for tokens that are **not masked**,
1204
+ - 0 for tokens that are **masked**.
1205
+ [What are attention masks?](../glossary#attention-mask)
1206
+ position_ids (`torch.LongTensor` of shape `(batch_size,
1207
+ sequence_length)`, *optional*):
1208
+ Indices of positions of each input sequence tokens in the position
1209
+ embeddings. Selected in the range `[0,
1210
+ config.max_position_embeddings - 1]`.
1211
+ [What are position IDs?](../glossary#position-ids)
1212
+ output_attentions (`bool`, *optional*):
1213
+ Whether or not to return the attentions tensors of all attention
1214
+ layers. See `attentions` under returned tensors for more detail.
1215
+ output_hidden_states (`bool`, *optional*):
1216
+ Whether or not to return the hidden states of all layers. See
1217
+ `hidden_states` under returned tensors for more detail.
1218
+ return_dict (`bool`, *optional*):
1219
+ Whether or not to return a [`~utils.ModelOutput`] instead of a
1220
+ plain tuple.
1221
+"""
1222
+
1223
+SIGLIP_VISION_INPUTS_DOCSTRING = r"""
1224
+ Args:
1225
+ pixel_values (`torch.FloatTensor` of shape `(batch_size,
1226
+ num_channels, height, width)`):
1227
+ Pixel values. Padding will be ignored by default should you
1228
+ provide it. Pixel values can be obtained using
1229
+ [`AutoImageProcessor`]. See [`CLIPImageProcessor.__call__`]
1230
+ for details.
1231
+ output_attentions (`bool`, *optional*):
1232
+ Whether or not to return the attentions tensors of all attention
1233
+ layers. See `attentions` under returned
1234
+ tensors for more detail.
1235
+ output_hidden_states (`bool`, *optional*):
1236
+ Whether or not to return the hidden states of all layers. See
1237
+ `hidden_states` under returned tensors for more detail.
1238
+ return_dict (`bool`, *optional*):
1239
+ Whether or not to return a [`~utils.ModelOutput`] instead of a
1240
+ plain tuple.
1241
+"""
1242
+
1243
+SIGLIP_INPUTS_DOCSTRING = r"""
1244
+ Args:
1245
+ input_ids (`torch.LongTensor` of shape `(batch_size,
1246
+ sequence_length)`):
1247
+ Indices of input sequence tokens in the vocabulary. Padding
1248
+ will be ignored by default should you provide it.
1249
+ Indices can be obtained using [`AutoTokenizer`]. See
1250
+ [`PreTrainedTokenizer.encode`] and [`PreTrainedTokenizer.__call__`]
1251
+ for details. [What are input IDs?](../glossary#input-ids)
1252
+ attention_mask (`torch.Tensor` of shape `(batch_size, sequence_length)`
1253
+ , *optional*):
1254
+ Mask to avoid performing attention on padding token indices. Mask
1255
+ values selected in `[0, 1]`:
1256
+ - 1 for tokens that are **not masked**,
1257
+ - 0 for tokens that are **masked**.
1258
+ [What are attention masks?](../glossary#attention-mask)
1259
+ position_ids (`torch.LongTensor` of shape `(batch_size,
1260
+ sequence_length)`, *optional*):
1261
+ Indices of positions of each input sequence tokens in the position
1262
+ embeddings. Selected in the range `[0,
1263
+ config.max_position_embeddings - 1]`.
1264
+ [What are position IDs?](../glossary#position-ids)
1265
+ pixel_values (`torch.FloatTensor` of shape `(batch_size,
1266
+ num_channels, height, width)`):
1267
+ Pixel values. Padding will be ignored by default should you
1268
+ provide it. Pixel values can be obtained using
1269
+ [`AutoImageProcessor`]. See [`CLIPImageProcessor.__call__`]
1270
+ for details.
1271
+ return_loss (`bool`, *optional*):
1272
+ Whether or not to return the contrastive loss.
1273
+ output_attentions (`bool`, *optional*):
1274
+ Whether or not to return the attentions tensors of all attention
1275
+ layers. See `attentions` under returned tensors for more detail.
1276
+ output_hidden_states (`bool`, *optional*):
1277
+ Whether or not to return the hidden states of all layers. See
1278
+ `hidden_states` under returned tensors for
1279
+ more detail.
1280
+ return_dict (`bool`, *optional*):
1281
+ Whether or not to return a [`~utils.ModelOutput`] instead of a
1282
+ plain tuple.
1283
+"""
1284
+
1285
+
1286
+# Copied from transformers.models.clip.modeling_clip.CLIPEncoder with
1287
+# CLIP->Siglip
1288
+class SiglipEncoder(nn.Module):
1289
+ """
1290
+ Transformer encoder consisting of `config.num_hidden_layers`
1291
+ self attention layers. Each layer is a [`SiglipEncoderLayer`].
1292
+ Args:
1293
+ config: SiglipConfig
1294
+ """
1295
+
1296
+ def __init__(self, config: SiglipConfig):
1297
+ super().__init__()
1298
+ self.config = config
1299
+ self.layers = nn.ModuleList([
1300
+ SiglipEncoderLayer(config) for _ in range(config.num_hidden_layers)
1301
+ ])
1302
+ self.gradient_checkpointing = False
1303
+
1304
+ # Ignore copy
1305
+ def forward(
1306
+ self,
1307
+ inputs_embeds,
1308
+ attention_mask: Optional[torch.Tensor] = None,
1309
+ output_attentions: Optional[bool] = None,
1310
+ output_hidden_states: Optional[bool] = None,
1311
+ return_dict: Optional[bool] = None,
1312
+ ) -> Union[Tuple, BaseModelOutput]:
1313
+ r"""
1314
+ Args:
1315
+ inputs_embeds (`torch.FloatTensor` of shape `(batch_size,
1316
+ sequence_length, hidden_size)`):
1317
+ Optionally, instead of passing `input_ids` you can choose to
1318
+ directly pass an embedded representation.
1319
+ This is useful if you want more control over how to convert
1320
+ `input_ids` indices into associated vectors
1321
+ than the model's internal embedding lookup matrix.
1322
+ attention_mask (`torch.Tensor` of shape `(batch_size,
1323
+ sequence_length)`, *optional*):
1324
+ Mask to avoid performing attention on padding token indices.
1325
+ Mask values selected in `[0, 1]`:
1326
+ - 1 for tokens that are **not masked**,
1327
+ - 0 for tokens that are **masked**.
1328
+ [What are attention masks?](../glossary#attention-mask)
1329
+ output_attentions (`bool`, *optional*):
1330
+ Whether or not to return the attentions tensors of all
1331
+ attention layers. See `attentions` under returned tensors for
1332
+ more detail.
1333
+ output_hidden_states (`bool`, *optional*):
1334
+ Whether or not to return the hidden states of all layers. See
1335
+ `hidden_states` under returned tensors for more detail.
1336
+ return_dict (`bool`, *optional*):
1337
+ Whether or not to return a [`~utils.ModelOutput`] instead of a
1338
+ plain tuple.
1339
+ """
1340
+ output_attentions = output_attentions if output_attentions \
1341
+ is not None else self.config.output_attentions
1342
+ output_hidden_states = (output_hidden_states
1343
+ if output_hidden_states is not None else
1344
+ self.config.output_hidden_states)
1345
+ return_dict = return_dict if return_dict is not None else \
1346
+ self.config.use_return_dict
1347
+
1348
+ encoder_states = () if output_hidden_states else None
1349
+ all_attentions = () if output_attentions else None
1350
+
1351
+ hidden_states = inputs_embeds
1352
+ for encoder_layer in self.layers:
1353
+ if output_hidden_states:
1354
+ encoder_states = encoder_states + (hidden_states, )
1355
+ if self.gradient_checkpointing and self.training:
1356
+ layer_outputs = self._gradient_checkpointing_func(
1357
+ encoder_layer.__call__,
1358
+ hidden_states,
1359
+ attention_mask,
1360
+ output_attentions,
1361
+ )
1362
+ else:
1363
+ layer_outputs = encoder_layer(
1364
+ hidden_states,
1365
+ attention_mask,
1366
+ output_attentions=output_attentions,
1367
+ )
1368
+
1369
+ hidden_states = layer_outputs[0]
1370
+
1371
+ if output_attentions:
1372
+ all_attentions = all_attentions + (layer_outputs[1], )
1373
+
1374
+ if output_hidden_states:
1375
+ encoder_states = encoder_states + (hidden_states, )
1376
+
1377
+ if not return_dict:
1378
+ return tuple(
1379
+ v for v in [hidden_states, encoder_states, all_attentions]
1380
+ if v is not None)
1381
+ return BaseModelOutput(last_hidden_state=hidden_states,
1382
+ hidden_states=encoder_states,
1383
+ attentions=all_attentions)
1384
+
1385
+
1386
+class SiglipTextTransformer(nn.Module):
1387
+
1388
+ def __init__(self, config: SiglipTextConfig):
1389
+ super().__init__()
1390
+ self.config = config
1391
+ embed_dim = config.hidden_size
1392
+ self.embeddings = SiglipTextEmbeddings(config)
1393
+ self.encoder = SiglipEncoder(config)
1394
+ self.final_layer_norm = nn.LayerNorm(embed_dim,
1395
+ eps=config.layer_norm_eps)
1396
+
1397
+ self.head = nn.Linear(embed_dim, embed_dim)
1398
+
1399
+ @add_start_docstrings_to_model_forward(SIGLIP_TEXT_INPUTS_DOCSTRING)
1400
+ @replace_return_docstrings(output_type=BaseModelOutputWithPooling,
1401
+ config_class=SiglipTextConfig)
1402
+ def forward(
1403
+ self,
1404
+ input_ids: Optional[torch.Tensor] = None,
1405
+ attention_mask: Optional[torch.Tensor] = None,
1406
+ position_ids: Optional[torch.Tensor] = None,
1407
+ output_attentions: Optional[bool] = None,
1408
+ output_hidden_states: Optional[bool] = None,
1409
+ return_dict: Optional[bool] = None,
1410
+ ) -> Union[Tuple, BaseModelOutputWithPooling]:
1411
+ r"""
1412
+ Returns:
1413
+ """
1414
+ output_attentions = output_attentions if output_attentions \
1415
+ is not None else self.config.output_attentions
1416
+ output_hidden_states = (output_hidden_states
1417
+ if output_hidden_states \
1418
+ is not None else
1419
+ self.config.output_hidden_states)
1420
+ return_dict = return_dict if return_dict is not None else \
1421
+ self.config.use_return_dict
1422
+
1423
+ if input_ids is None:
1424
+ raise ValueError("You have to specify input_ids")
1425
+
1426
+ input_shape = input_ids.size()
1427
+ input_ids = input_ids.view(-1, input_shape[-1])
1428
+
1429
+ hidden_states = self.embeddings(input_ids=input_ids,
1430
+ position_ids=position_ids)
1431
+
1432
+ # note: SigLIP's text model does not use a causal mask, unlike the
1433
+ # original CLIP model.
1434
+ # expand attention_mask
1435
+ if attention_mask is not None:
1436
+ # [batch_size, seq_len] ->
1437
+ # [batch_size, 1, tgt_seq_len, src_seq_len]
1438
+ attention_mask = _prepare_4d_attention_mask(
1439
+ attention_mask, hidden_states.dtype)
1440
+
1441
+ encoder_outputs = self.encoder(
1442
+ inputs_embeds=hidden_states,
1443
+ attention_mask=attention_mask,
1444
+ output_attentions=output_attentions,
1445
+ output_hidden_states=output_hidden_states,
1446
+ return_dict=return_dict,
1447
+ )
1448
+
1449
+ last_hidden_state = encoder_outputs[0]
1450
+ last_hidden_state = self.final_layer_norm(last_hidden_state)
1451
+
1452
+ # Assuming "sticky" EOS tokenization, last token is always EOS.
1453
+ pooled_output = last_hidden_state[:, -1, :]
1454
+ pooled_output = self.head(pooled_output)
1455
+
1456
+ if not return_dict:
1457
+ return (last_hidden_state, pooled_output) + encoder_outputs[1:]
1458
+
1459
+ return BaseModelOutputWithPooling(
1460
+ last_hidden_state=last_hidden_state,
1461
+ pooler_output=pooled_output,
1462
+ hidden_states=encoder_outputs.hidden_states,
1463
+ attentions=encoder_outputs.attentions,
1464
+ )
1465
+
1466
+
1467
+@add_start_docstrings(
1468
+ """The text model from SigLIP without any head or projection on top.""",
1469
+ SIGLIP_START_DOCSTRING,
1470
+)
1471
+class SiglipTextModel(SiglipPreTrainedModel):
1472
+ config_class = SiglipTextConfig
1473
+
1474
+ _no_split_modules = ["SiglipTextEmbeddings", "SiglipEncoderLayer"]
1475
+
1476
+ def __init__(self, config: SiglipTextConfig):
1477
+ super().__init__(config)
1478
+ self.text_model = SiglipTextTransformer(config)
1479
+ # Initialize weights and apply final processing
1480
+ self.post_init()
1481
+
1482
+ def get_input_embeddings(self) -> nn.Module:
1483
+ return self.text_model.embeddings.token_embedding
1484
+
1485
+ def set_input_embeddings(self, value):
1486
+ self.text_model.embeddings.token_embedding = value
1487
+
1488
+ @add_start_docstrings_to_model_forward(SIGLIP_TEXT_INPUTS_DOCSTRING)
1489
+ @replace_return_docstrings(output_type=BaseModelOutputWithPooling,
1490
+ config_class=SiglipTextConfig)
1491
+ def forward(
1492
+ self,
1493
+ input_ids: Optional[torch.Tensor] = None,
1494
+ attention_mask: Optional[torch.Tensor] = None,
1495
+ position_ids: Optional[torch.Tensor] = None,
1496
+ output_attentions: Optional[bool] = None,
1497
+ output_hidden_states: Optional[bool] = None,
1498
+ return_dict: Optional[bool] = None,
1499
+ ) -> Union[Tuple, BaseModelOutputWithPooling]:
1500
+ r"""
1501
+ Returns:
1502
+ Examples:
1503
+ ```python
1504
+ >>> from transformers import AutoTokenizer, SiglipTextModel
1505
+ >>> model = SiglipTextModel.
1506
+ from_pretrained("google/siglip-base-patch16-224")
1507
+ >>> tokenizer = AutoTokenizer.
1508
+ from_pretrained("google/siglip-base-patch16-224")
1509
+ >>> # important: make sure to set padding="max_length"
1510
+ as that's how the model was trained
1511
+ >>> inputs = tokenizer(["a photo of a cat", "a photo of a dog"],
1512
+ padding="max_length", return_tensors="pt")
1513
+ >>> outputs = model(**inputs)
1514
+ >>> last_hidden_state = outputs.last_hidden_state
1515
+ >>> pooled_output = outputs.pooler_output # pooled (EOS token)
1516
+ states
1517
+ ```"""
1518
+ return_dict = return_dict if return_dict is not None else \
1519
+ self.config.use_return_dict
1520
+
1521
+ return self.text_model(
1522
+ input_ids=input_ids,
1523
+ attention_mask=attention_mask,
1524
+ position_ids=position_ids,
1525
+ output_attentions=output_attentions,
1526
+ output_hidden_states=output_hidden_states,
1527
+ return_dict=return_dict,
1528
+ )
1529
+
1530
+
1531
+class SiglipVisionTransformer(nn.Module):
1532
+
1533
+ def __init__(self, config: SiglipVisionConfig):
1534
+ super().__init__()
1535
+ self.config = config
1536
+ embed_dim = config.hidden_size
1537
+
1538
+ self.embeddings = SiglipVisionEmbeddings(config)
1539
+ self.encoder = SiglipEncoder(config)
1540
+ self.post_layernorm = nn.LayerNorm(embed_dim,
1541
+ eps=config.layer_norm_eps)
1542
+ self.head = SiglipMultiheadAttentionPoolingHead(config)
1543
+
1544
+ @add_start_docstrings_to_model_forward(SIGLIP_VISION_INPUTS_DOCSTRING)
1545
+ @replace_return_docstrings(output_type=BaseModelOutputWithPooling,
1546
+ config_class=SiglipVisionConfig)
1547
+ def forward(
1548
+ self,
1549
+ pixel_values,
1550
+ patch_attention_mask: Optional[torch.BoolTensor] = None,
1551
+ output_attentions: Optional[bool] = None,
1552
+ output_hidden_states: Optional[bool] = None,
1553
+ return_dict: Optional[bool] = None,
1554
+ ) -> Union[Tuple, BaseModelOutputWithPooling]:
1555
+ r"""
1556
+ Returns:
1557
+ """
1558
+ output_attentions = output_attentions if output_attentions is not None\
1559
+ else self.config.output_attentions
1560
+ output_hidden_states = (output_hidden_states
1561
+ if output_hidden_states is not None else
1562
+ self.config.output_hidden_states)
1563
+ return_dict = return_dict if return_dict is not None \
1564
+ else self.config.use_return_dict
1565
+
1566
+ batch_size = pixel_values.size(0)
1567
+ if patch_attention_mask is None:
1568
+ patch_attention_mask = torch.ones(
1569
+ size=(
1570
+ batch_size,
1571
+ pixel_values.size(2) // self.config.patch_size,
1572
+ pixel_values.size(3) // self.config.patch_size,
1573
+ ),
1574
+ dtype=torch.bool,
1575
+ device=pixel_values.device,
1576
+ )
1577
+
1578
+ hidden_states = self.embeddings(
1579
+ pixel_values=pixel_values,
1580
+ patch_attention_mask=patch_attention_mask)
1581
+
1582
+ patch_attention_mask = patch_attention_mask.view(batch_size, -1)
1583
+ # The call to `_upad_input` in `_flash_attention_forward` is expensive
1584
+ # So when the `patch_attention_mask` is full of 1s (i.e. attending
1585
+ # to the whole sequence), avoiding passing the attention_mask, which
1586
+ # is equivalent to attending to the full sequence
1587
+ if not torch.any(~patch_attention_mask):
1588
+ attention_mask = None
1589
+ else:
1590
+ attention_mask = (_prepare_4d_attention_mask(
1591
+ patch_attention_mask, hidden_states.dtype)
1592
+ if not self.config._flash_attn_2_enabled else
1593
+ patch_attention_mask)
1594
+
1595
+ encoder_outputs = self.encoder(
1596
+ inputs_embeds=hidden_states,
1597
+ attention_mask=attention_mask,
1598
+ output_attentions=output_attentions,
1599
+ output_hidden_states=output_hidden_states,
1600
+ return_dict=return_dict,
1601
+ )
1602
+
1603
+ last_hidden_state = encoder_outputs[0]
1604
+ last_hidden_state = self.post_layernorm(last_hidden_state)
1605
+
1606
+ pooled_output = self.head(
1607
+ hidden_state=last_hidden_state,
1608
+ attention_mask=patch_attention_mask,
1609
+ )
1610
+
1611
+ if not return_dict:
1612
+ return (last_hidden_state, pooled_output) + encoder_outputs[1:]
1613
+
1614
+ return BaseModelOutputWithPooling(
1615
+ last_hidden_state=last_hidden_state,
1616
+ pooler_output=pooled_output,
1617
+ hidden_states=encoder_outputs.hidden_states,
1618
+ attentions=encoder_outputs.attentions,
1619
+ )
1620
+
1621
+
1622
+class SiglipMultiheadAttentionPoolingHead(nn.Module):
1623
+ """Multihead Attention Pooling."""
1624
+
1625
+ def __init__(self, config: SiglipVisionConfig):
1626
+ super().__init__()
1627
+
1628
+ self.probe = nn.Parameter(torch.randn(1, 1, config.hidden_size))
1629
+ self.attention = torch.nn.MultiheadAttention(
1630
+ config.hidden_size, config.num_attention_heads, batch_first=True)
1631
+ self.layernorm = nn.LayerNorm(config.hidden_size,
1632
+ eps=config.layer_norm_eps)
1633
+ self.mlp = SiglipMLP(config)
1634
+
1635
+ def forward(self, hidden_state, attention_mask):
1636
+ batch_size = hidden_state.shape[0]
1637
+ probe = self.probe.repeat(batch_size, 1, 1)
1638
+
1639
+ hidden_state = self.attention(query=probe,
1640
+ key=hidden_state,
1641
+ value=hidden_state,
1642
+ key_padding_mask=~attention_mask)[0]
1643
+
1644
+ residual = hidden_state
1645
+ hidden_state = self.layernorm(hidden_state)
1646
+ hidden_state = residual + self.mlp(hidden_state)
1647
+
1648
+ return hidden_state[:, 0]
1649
+
1650
+
1651
+@add_start_docstrings(
1652
+ """The vision model from SigLIP without any head or projection on top.""",
1653
+ SIGLIP_START_DOCSTRING,
1654
+)
1655
+class SiglipVisionModel(SiglipPreTrainedModel):
1656
+ config_class = SiglipVisionConfig
1657
+ main_input_name = "pixel_values"
1658
+
1659
+ def __init__(self, config: SiglipVisionConfig):
1660
+ super().__init__(config)
1661
+
1662
+ self.vision_model = SiglipVisionTransformer(config)
1663
+
1664
+ # Initialize weights and apply final processing
1665
+ self.post_init()
1666
+
1667
+ def get_input_embeddings(self) -> nn.Module:
1668
+ return self.vision_model.embeddings.patch_embedding
1669
+
1670
+ @add_start_docstrings_to_model_forward(SIGLIP_VISION_INPUTS_DOCSTRING)
1671
+ @replace_return_docstrings(output_type=BaseModelOutputWithPooling,
1672
+ config_class=SiglipVisionConfig)
1673
+ def forward(
1674
+ self,
1675
+ pixel_values,
1676
+ patch_attention_mask: Optional[torch.BoolTensor] = None,
1677
+ output_attentions: Optional[bool] = None,
1678
+ output_hidden_states: Optional[bool] = None,
1679
+ return_dict: Optional[bool] = None,
1680
+ ) -> Union[Tuple, BaseModelOutputWithPooling]:
1681
+ r"""
1682
+ Returns:
1683
+ Examples:
1684
+ ```python
1685
+ >>> from PIL import Image
1686
+ >>> import requests
1687
+ >>> from transformers import AutoProcessor, SiglipVisionModel
1688
+ >>> model = SiglipVisionModel.from_pretrained(
1689
+ "google/siglip-base-patch16-224")
1690
+ >>> processor = AutoProcessor.from_pretrained(
1691
+ "google/siglip-base-patch16-224")
1692
+ >>> url =
1693
+ "http://images.cocodataset.org/val2017/000000039769.jpg"
1694
+ >>> image = Image.open(requests.get(url, stream=True).raw)
1695
+ >>> inputs = processor(images=image, return_tensors="pt")
1696
+ >>> outputs = model(**inputs)
1697
+ >>> last_hidden_state = outputs.last_hidden_state
1698
+ >>> pooled_output = outputs.pooler_output # pooled features
1699
+ ```"""
1700
+ return_dict = return_dict if return_dict is not None \
1701
+ else self.config.use_return_dict
1702
+
1703
+ return self.vision_model(
1704
+ pixel_values=pixel_values,
1705
+ patch_attention_mask=patch_attention_mask,
1706
+ output_attentions=output_attentions,
1707
+ output_hidden_states=output_hidden_states,
1708
+ return_dict=return_dict,
1709
+ )
1710
+
1711
+
1712
+@add_start_docstrings(SIGLIP_START_DOCSTRING)
1713
+class SiglipModel(SiglipPreTrainedModel):
1714
+ config_class = SiglipConfig
1715
+
1716
+ def __init__(self, config: SiglipConfig):
1717
+ super().__init__(config)
1718
+
1719
+ if not isinstance(config.text_config, SiglipTextConfig):
1720
+ raise ValueError("config.text_config is expected to be of type "
1721
+ f"SiglipTextConfig but is of type"
1722
+ f" {type(config.text_config)}.")
1723
+
1724
+ if not isinstance(config.vision_config, SiglipVisionConfig):
1725
+ raise ValueError("config.vision_config is expected to be of type "
1726
+ "SiglipVisionConfig but is of type"
1727
+ f" {type(config.vision_config)}.")
1728
+
1729
+ text_config = config.text_config
1730
+ vision_config = config.vision_config
1731
+
1732
+ self.text_model = SiglipTextTransformer(text_config)
1733
+ self.vision_model = SiglipVisionTransformer(vision_config)
1734
+
1735
+ self.logit_scale = nn.Parameter(torch.randn(1))
1736
+ self.logit_bias = nn.Parameter(torch.randn(1))
1737
+
1738
+ # Initialize weights and apply final processing
1739
+ self.post_init()
1740
+
1741
+ @add_start_docstrings_to_model_forward(SIGLIP_TEXT_INPUTS_DOCSTRING)
1742
+ def get_text_features(
1743
+ self,
1744
+ input_ids: Optional[torch.Tensor] = None,
1745
+ attention_mask: Optional[torch.Tensor] = None,
1746
+ position_ids: Optional[torch.Tensor] = None,
1747
+ output_attentions: Optional[bool] = None,
1748
+ output_hidden_states: Optional[bool] = None,
1749
+ return_dict: Optional[bool] = None,
1750
+ ) -> torch.FloatTensor:
1751
+ r"""
1752
+ Returns:
1753
+ text_features (`torch.FloatTensor` of shape `(batch_size,
1754
+ output_dim`): The text embeddings obtained by
1755
+ applying the projection layer to the pooled output
1756
+ of [`SiglipTextModel`].
1757
+ Examples:
1758
+ ```python
1759
+ >>> from transformers import AutoTokenizer, AutoModel
1760
+ >>> import torch
1761
+ >>> model = AutoModel.from_pretrained(
1762
+ "google/siglip-base-patch16-224")
1763
+ >>> tokenizer = AutoTokenizer.from_pretrained(
1764
+ "google/siglip-base-patch16-224")
1765
+ >>> # important: make sure to set padding="max_length" as that's
1766
+ how the model was trained
1767
+ >>> inputs = tokenizer(["a photo of a cat", "a photo of a dog"],
1768
+ padding="max_length", return_tensors="pt")
1769
+ >>> with torch.no_grad():
1770
+ ... text_features = model.get_text_features(**inputs)
1771
+ ```"""
1772
+ # Use SigLIP model's config for some fields (if specified) instead
1773
+ # of those of vision & text components.
1774
+ output_attentions = output_attentions if output_attentions is not None\
1775
+ else self.config.output_attentions
1776
+ output_hidden_states = (output_hidden_states
1777
+ if output_hidden_states is not None else
1778
+ self.config.output_hidden_states)
1779
+ return_dict = return_dict if return_dict is not None \
1780
+ else self.config.use_return_dict
1781
+
1782
+ text_outputs = self.text_model(
1783
+ input_ids=input_ids,
1784
+ attention_mask=attention_mask,
1785
+ position_ids=position_ids,
1786
+ output_attentions=output_attentions,
1787
+ output_hidden_states=output_hidden_states,
1788
+ return_dict=return_dict,
1789
+ )
1790
+
1791
+ pooled_output = text_outputs[1]
1792
+
1793
+ return pooled_output
1794
+
1795
+ @add_start_docstrings_to_model_forward(SIGLIP_VISION_INPUTS_DOCSTRING)
1796
+ def get_image_features(
1797
+ self,
1798
+ pixel_values: Optional[torch.FloatTensor] = None,
1799
+ output_attentions: Optional[bool] = None,
1800
+ output_hidden_states: Optional[bool] = None,
1801
+ return_dict: Optional[bool] = None,
1802
+ ) -> torch.FloatTensor:
1803
+ r"""
1804
+ Returns:
1805
+ image_features (`torch.FloatTensor` of shape `(batch_size,
1806
+ output_dim`): The image embeddings obtained by applying the
1807
+ projection layer to the pooled output of [`SiglipVisionModel`].
1808
+ Examples:
1809
+ ```python
1810
+ >>> from PIL import Image
1811
+ >>> import requests
1812
+ >>> from transformers import AutoProcessor, AutoModel
1813
+ >>> import torch
1814
+ >>> model = AutoModel.from_pretrained("google/siglip-base-patch16-224")
1815
+ >>> processor = AutoProcessor.from_pretrained(
1816
+ "google/siglip-base-patch16-224")
1817
+ >>> url = "http://images.cocodataset.org/val2017/000000039769.jpg"
1818
+ >>> image = Image.open(requests.get(url, stream=True).raw)
1819
+ >>> inputs = processor(images=image, return_tensors="pt")
1820
+ >>> with torch.no_grad():
1821
+ ... image_features = model.get_image_features(**inputs)
1822
+ ```"""
1823
+ # Use SiglipModel's config for some fields (if specified) instead
1824
+ # of those of vision & text components.
1825
+ output_attentions = output_attentions if output_attentions \
1826
+ is not None else self.config.output_attentions
1827
+ output_hidden_states = (output_hidden_states
1828
+ if output_hidden_states is not None else
1829
+ self.config.output_hidden_states)
1830
+ return_dict = return_dict if return_dict is not None else \
1831
+ self.config.use_return_dict
1832
+
1833
+ vision_outputs = self.vision_model(
1834
+ pixel_values=pixel_values,
1835
+ output_attentions=output_attentions,
1836
+ output_hidden_states=output_hidden_states,
1837
+ return_dict=return_dict,
1838
+ )
1839
+
1840
+ pooled_output = vision_outputs[1]
1841
+
1842
+ return pooled_output
1843
+
1844
+ @add_start_docstrings_to_model_forward(SIGLIP_INPUTS_DOCSTRING)
1845
+ @replace_return_docstrings(output_type=SiglipOutput,
1846
+ config_class=SiglipConfig)
1847
+ def forward(
1848
+ self,
1849
+ input_ids: Optional[torch.LongTensor] = None,
1850
+ pixel_values: Optional[torch.FloatTensor] = None,
1851
+ attention_mask: Optional[torch.Tensor] = None,
1852
+ position_ids: Optional[torch.LongTensor] = None,
1853
+ return_loss: Optional[bool] = None,
1854
+ output_attentions: Optional[bool] = None,
1855
+ output_hidden_states: Optional[bool] = None,
1856
+ return_dict: Optional[bool] = None,
1857
+ ) -> Union[Tuple, SiglipOutput]:
1858
+ r"""
1859
+ Returns:
1860
+ Examples:
1861
+ ```python
1862
+ >>> from PIL import Image
1863
+ >>> import requests
1864
+ >>> from transformers import AutoProcessor, AutoModel
1865
+ >>> import torch
1866
+ >>> model = AutoModel.from_pretrained("google/siglip-base-patch16-224")
1867
+ >>> processor = AutoProcessor.from_pretrained(
1868
+ "google/siglip-base-patch16-224")
1869
+ >>> url = "http://images.cocodataset.org/val2017/000000039769.jpg"
1870
+ >>> image = Image.open(requests.get(url, stream=True).raw)
1871
+ >>> texts = ["a photo of 2 cats", "a photo of 2 dogs"]
1872
+ >>> # important: we pass `padding=max_length` since the model was
1873
+ trained with this
1874
+ >>> inputs = processor(text=texts, images=image,
1875
+ padding="max_length", return_tensors="pt")
1876
+ >>> with torch.no_grad():
1877
+ ... outputs = model(**inputs)
1878
+ >>> logits_per_image = outputs.logits_per_image
1879
+ >>> probs = torch.sigmoid(logits_per_image) # these are the
1880
+ probabilities
1881
+ >>> print(f"{probs[0][0]:.1%} that image 0 is '{texts[0]}'")
1882
+ 31.9% that image 0 is 'a photo of 2 cats'
1883
+ ```"""
1884
+ # Use SigLIP model's config for some fields (if specified) instead of
1885
+ # those of vision & text components.
1886
+ output_attentions = output_attentions if output_attentions \
1887
+ is not None else self.config.output_attentions
1888
+ output_hidden_states = (output_hidden_states
1889
+ if output_hidden_states is not None else
1890
+ self.config.output_hidden_states)
1891
+ return_dict = return_dict if return_dict is not None else \
1892
+ self.config.use_return_dict
1893
+
1894
+ vision_outputs = self.vision_model(
1895
+ pixel_values=pixel_values,
1896
+ output_attentions=output_attentions,
1897
+ output_hidden_states=output_hidden_states,
1898
+ return_dict=return_dict,
1899
+ )
1900
+
1901
+ text_outputs = self.text_model(
1902
+ input_ids=input_ids,
1903
+ attention_mask=attention_mask,
1904
+ position_ids=position_ids,
1905
+ output_attentions=output_attentions,
1906
+ output_hidden_states=output_hidden_states,
1907
+ return_dict=return_dict,
1908
+ )
1909
+
1910
+ image_embeds = vision_outputs[1]
1911
+ text_embeds = text_outputs[1]
1912
+
1913
+ # normalized features
1914
+ image_embeds = image_embeds / image_embeds.norm(
1915
+ p=2, dim=-1, keepdim=True)
1916
+ text_embeds = text_embeds / text_embeds.norm(p=2, dim=-1, keepdim=True)
1917
+
1918
+ # cosine similarity as logits
1919
+ logits_per_text = torch.matmul(text_embeds, image_embeds.t(
1920
+ )) * self.logit_scale.exp() + self.logit_bias
1921
+ logits_per_image = logits_per_text.t()
1922
+
1923
+ loss = None
1924
+ if return_loss:
1925
+ raise NotImplementedError("SigLIP loss to be implemented")
1926
+
1927
+ if not return_dict:
1928
+ output = (logits_per_image, logits_per_text, text_embeds,
1929
+ image_embeds, text_outputs, vision_outputs)
1930
+ return ((loss, ) + output) if loss is not None else output
1931
+
1932
+ return SiglipOutput(
1933
+ loss=loss,
1934
+ logits_per_image=logits_per_image,
1935
+ logits_per_text=logits_per_text,
1936
+ text_embeds=text_embeds,
1937
+ image_embeds=image_embeds,
1938
+ text_model_output=text_outputs,
1939
+ vision_model_output=vision_outputs,
1940
+ )
1941
+
1942
+
1943
+def get_siglip_vision_model(_flash_attn_2_enabled=True, **kwargs):
1944
+ siglip_vision_config = {
1945
+ "hidden_size": 1152,
1946
+ "image_size": 448,
1947
+ "intermediate_size": 4304,
1948
+ "model_type": "siglip_vision_model",
1949
+ "num_attention_heads": 16,
1950
+ "num_hidden_layers": 27,
1951
+ "patch_size": 14,
1952
+ }
1953
+
1954
+ # Detect attention implementation.
1955
+ attn_backend: _Backend = get_vit_attn_backend(support_fa=True)
1956
+ if attn_backend != _Backend.FLASH_ATTN:
1957
+ _flash_attn_2_enabled = False
1958
+
1959
+ model_config = SiglipVisionConfig(
1960
+ **siglip_vision_config,
1961
+ _flash_attn_2_enabled=_flash_attn_2_enabled,
1962
+ **kwargs)
1963
+
1964
+ vision_model = SiglipVisionModel(model_config).vision_model
1965
+
1966
+ return vision_model